HHH-16695 add enableFetchProfile() to XxxxIdLoadAccess

This commit is contained in:
Gavin King 2023-07-08 22:41:46 +02:00
parent 2e351831f1
commit 09f110254f
13 changed files with 196 additions and 106 deletions

View File

@ -104,6 +104,10 @@ public interface IdentifierLoadAccess<T> {
*/
IdentifierLoadAccess<T> with(RootGraph<T> graph, GraphSemantic semantic);
IdentifierLoadAccess<T> enableFetchProfile(String profileName);
IdentifierLoadAccess<T> disableFetchProfile(String profileName);
/**
* Return the persistent instance with the given identifier, assuming
* that the instance exists. This method might return a proxied instance

View File

@ -44,6 +44,10 @@ public interface NaturalIdLoadAccess<T> {
*/
NaturalIdLoadAccess<T> with(LockOptions lockOptions);
NaturalIdLoadAccess<T> enableFetchProfile(String profileName);
NaturalIdLoadAccess<T> disableFetchProfile(String profileName);
/**
* Add a {@link org.hibernate.annotations.NaturalId @NaturalId}
* attribute value in a typesafe way.

View File

@ -37,6 +37,10 @@ public interface SimpleNaturalIdLoadAccess<T> {
*/
SimpleNaturalIdLoadAccess<T> with(LockOptions lockOptions);
SimpleNaturalIdLoadAccess<T> enableFetchProfile(String profileName);
SimpleNaturalIdLoadAccess<T> disableFetchProfile(String profileName);
/**
* For entities with mutable natural ids, should Hibernate perform
* "synchronization" prior to performing lookups? The default is

View File

@ -260,6 +260,19 @@ public class LoadQueryInfluencers implements Serializable {
}
}
@Internal
public HashSet<String> adjustFetchProfiles(Set<String> disabledFetchProfiles, Set<String> enabledFetchProfiles) {
final HashSet<String> oldFetchProfiles =
hasEnabledFetchProfiles() ? new HashSet<>( enabledFetchProfileNames ) : null;
if ( disabledFetchProfiles != null ) {
enabledFetchProfileNames.removeAll( disabledFetchProfiles );
}
if ( enabledFetchProfiles != null ) {
enabledFetchProfileNames.addAll( enabledFetchProfiles );
}
return oldFetchProfiles;
}
@Internal
public void setEnabledFetchProfileNames(HashSet<String> enabledFetchProfileNames) {
this.enabledFetchProfileNames = enabledFetchProfileNames;

View File

@ -13,7 +13,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Consumer;

View File

@ -7,13 +7,16 @@
package org.hibernate.loader.internal;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.IdentifierLoadAccess;
import org.hibernate.LockOptions;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
@ -39,6 +42,9 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
private LockOptions lockOptions;
private boolean synchronizationEnabled = true;
private Set<String> enabledFetchProfiles;
private Set<String> disabledFetchProfiles;
protected BaseNaturalIdLoadAccessImpl(LoadAccessContext context, EntityMappingType entityDescriptor) {
this.context = context;
this.entityDescriptor = entityDescriptor;
@ -54,6 +60,28 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
return lockOptions;
}
public Object enableFetchProfile(String profileName) {
if ( enabledFetchProfiles == null ) {
enabledFetchProfiles = new HashSet<>();
}
enabledFetchProfiles.add( profileName );
if ( disabledFetchProfiles != null ) {
disabledFetchProfiles.remove( profileName );
}
return this;
}
public Object disableFetchProfile(String profileName) {
if ( disabledFetchProfiles == null ) {
disabledFetchProfiles = new HashSet<>();
}
disabledFetchProfiles.add( profileName );
if ( enabledFetchProfiles != null ) {
enabledFetchProfiles.remove( profileName );
}
return this;
}
public boolean isSynchronizationEnabled() {
return synchronizationEnabled;
}
@ -100,7 +128,9 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
}
final PersistenceContext persistenceContext = context.getSession().getPersistenceContextInternal();
final Collection<?> cachedPkResolutions = persistenceContext.getNaturalIdResolutions().getCachedPkResolutions( entityPersister() );
final Collection<?> cachedPkResolutions =
persistenceContext.getNaturalIdResolutions()
.getCachedPkResolutions( entityPersister() );
for ( Object pk : cachedPkResolutions ) {
final EntityKey entityKey = context.getSession().generateEntityKey( pk, entityPersister() );
final Object entity = persistenceContext.getEntity( entityKey );
@ -126,11 +156,7 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
continue;
}
persistenceContext.getNaturalIdResolutions().handleSynchronization(
pk,
entity,
entityPersister()
);
persistenceContext.getNaturalIdResolutions().handleSynchronization( pk, entity, entityPersister() );
}
}
@ -144,34 +170,32 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
final SessionImplementor session = context.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final Object cachedResolution = persistenceContext.getNaturalIdResolutions().findCachedIdByNaturalId(
normalizedNaturalIdValue,
entityPersister()
);
final Object cachedResolution =
persistenceContext.getNaturalIdResolutions()
.findCachedIdByNaturalId( normalizedNaturalIdValue, entityPersister() );
if ( cachedResolution == INVALID_NATURAL_ID_REFERENCE ) {
// the entity is deleted, although not yet flushed - return null
return null;
}
if ( cachedResolution != null ) {
return (T) getIdentifierLoadAccess().getReference( cachedResolution );
else {
if ( cachedResolution != null ) {
return (T) getIdentifierLoadAccess().getReference( cachedResolution );
}
else {
LoaderLogging.LOADER_LOGGER.debugf(
"Selecting entity identifier by natural-id for `#getReference` handling - %s : %s",
entityPersister().getEntityName(),
normalizedNaturalIdValue
);
final Object idFromDatabase =
entityPersister().getNaturalIdLoader()
.resolveNaturalIdToId( normalizedNaturalIdValue, session );
return idFromDatabase == null ? null : (T) getIdentifierLoadAccess().getReference( idFromDatabase );
}
}
LoaderLogging.LOADER_LOGGER.debugf(
"Selecting entity identifier by natural-id for `#getReference` handling - %s : %s",
entityPersister().getEntityName(),
normalizedNaturalIdValue
);
final Object idFromDatabase = entityPersister().getNaturalIdLoader().resolveNaturalIdToId( normalizedNaturalIdValue, session );
if ( idFromDatabase != null ) {
return (T) getIdentifierLoadAccess().getReference( idFromDatabase );
}
return null;
}
@SuppressWarnings("unchecked")
protected final T doLoad(Object normalizedNaturalIdValue) {
performAnyNeededCrossReferenceSynchronizations();
@ -181,53 +205,45 @@ public abstract class BaseNaturalIdLoadAccessImpl<T> implements NaturalIdLoadOpt
final SessionImplementor session = context.getSession();
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final Object cachedResolution = persistenceContext.getNaturalIdResolutions().findCachedIdByNaturalId(
normalizedNaturalIdValue,
entityPersister()
);
final Object cachedResolution =
persistenceContext.getNaturalIdResolutions()
.findCachedIdByNaturalId( normalizedNaturalIdValue, entityPersister() );
if ( cachedResolution == INVALID_NATURAL_ID_REFERENCE ) {
return null;
}
try {
final T loaded;
if ( cachedResolution != null ) {
loaded = (T) getIdentifierLoadAccess().load( cachedResolution );
}
else {
loaded = (T) entityPersister().getNaturalIdLoader().load( normalizedNaturalIdValue, this, session );
}
if ( loaded != null ) {
final EntityEntry entry;
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( loaded );
if ( lazyInitializer != null ) {
entry = persistenceContext.getEntry( lazyInitializer.getImplementation() );
}
else {
entry = persistenceContext.getEntry( loaded );
}
assert entry != null;
if ( entry.getStatus() == Status.DELETED ) {
return null;
else {
final LoadQueryInfluencers influencers = session.getLoadQueryInfluencers();
final HashSet<String> fetchProfiles =
influencers.adjustFetchProfiles( disabledFetchProfiles, enabledFetchProfiles );
try {
final T loaded = cachedResolution != null
? (T) getIdentifierLoadAccess().load(cachedResolution)
: (T) entityPersister().getNaturalIdLoader().load( normalizedNaturalIdValue, this, session );
if ( loaded != null ) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( loaded );
final EntityEntry entry = lazyInitializer != null
? persistenceContext.getEntry( lazyInitializer.getImplementation() )
: persistenceContext.getEntry( loaded );
assert entry != null;
if ( entry.getStatus() == Status.DELETED ) {
return null;
}
}
return loaded;
}
finally {
context.delayedAfterCompletion();
influencers.setEnabledFetchProfileNames( fetchProfiles );
}
return loaded;
}
finally {
context.delayedAfterCompletion();
}
}
protected final IdentifierLoadAccess<?> getIdentifierLoadAccess() {
final IdentifierLoadAccessImpl<?> identifierLoadAccess = new IdentifierLoadAccessImpl<>( context, entityPersister() );
if ( this.lockOptions != null ) {
identifierLoadAccess.with( lockOptions );
final IdentifierLoadAccessImpl<?> loadAccess = new IdentifierLoadAccessImpl<>( context, entityPersister() );
if ( lockOptions != null ) {
loadAccess.with( lockOptions );
}
return identifierLoadAccess;
return loadAccess;
}
protected LoadAccessContext getContext() {

View File

@ -6,7 +6,9 @@
*/
package org.hibernate.loader.internal;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import org.hibernate.CacheMode;
@ -17,6 +19,7 @@ import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInter
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.engine.spi.EffectiveEntityGraph;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.event.spi.EventSource;
@ -46,6 +49,8 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
private Boolean readOnly;
private RootGraphImplementor<T> rootGraph;
private GraphSemantic graphSemantic;
private Set<String> enabledFetchProfiles;
private Set<String> disabledFetchProfiles;
public IdentifierLoadAccessImpl(LoadAccessContext context, EntityPersister entityPersister) {
this.context = context;
@ -97,6 +102,9 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
}
try {
final LoadQueryInfluencers influencers = session.getLoadQueryInfluencers();
final HashSet<String> fetchProfiles =
influencers.adjustFetchProfiles( disabledFetchProfiles, enabledFetchProfiles );
final EffectiveEntityGraph effectiveEntityGraph =
session.getLoadQueryInfluencers().getEffectiveEntityGraph();
if ( graphSemantic != null ) {
@ -113,6 +121,7 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
if ( graphSemantic != null ) {
effectiveEntityGraph.clear();
}
influencers.setEnabledFetchProfileNames( fetchProfiles );
}
}
finally {
@ -173,7 +182,7 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
String entityName,
Boolean readOnly) {
if ( lockOptions != null ) {
final LoadEvent event = new LoadEvent(id, entityName, lockOptions, eventSource, readOnly);
final LoadEvent event = new LoadEvent( id, entityName, lockOptions, eventSource, readOnly );
context.fireLoad( event, LoadEventListener.LOAD );
return event.getResult();
}
@ -267,4 +276,28 @@ public class IdentifierLoadAccessImpl<T> implements IdentifierLoadAccess<T>, Jav
public TypeConfiguration getTypeConfiguration() {
return context.getSession().getSessionFactory().getTypeConfiguration();
}
@Override
public IdentifierLoadAccess<T> enableFetchProfile(String profileName) {
if ( enabledFetchProfiles == null ) {
enabledFetchProfiles = new HashSet<>();
}
enabledFetchProfiles.add( profileName );
if ( disabledFetchProfiles != null ) {
disabledFetchProfiles.remove( profileName );
}
return this;
}
@Override
public IdentifierLoadAccess<T> disableFetchProfile(String profileName) {
if ( disabledFetchProfiles == null ) {
disabledFetchProfiles = new HashSet<>();
}
disabledFetchProfiles.add( profileName );
if ( enabledFetchProfiles != null ) {
enabledFetchProfiles.remove( profileName );
}
return this;
}
}

View File

@ -75,4 +75,16 @@ public class NaturalIdLoadAccessImpl<T> extends BaseNaturalIdLoadAccessImpl<T> i
public Optional<T> loadOptional() {
return Optional.ofNullable( load() );
}
@Override
public NaturalIdLoadAccess<T> enableFetchProfile(String profileName) {
super.enableFetchProfile( profileName );
return this;
}
@Override
public NaturalIdLoadAccess<T> disableFetchProfile(String profileName) {
super.enableFetchProfile( profileName );
return this;
}
}

View File

@ -108,4 +108,17 @@ public class SimpleNaturalIdLoadAccessImpl<T>
public Optional<T> loadOptional(Object naturalIdValue) {
return Optional.ofNullable( load( naturalIdValue ) );
}
@Override
public SimpleNaturalIdLoadAccess<T> enableFetchProfile(String profileName) {
super.enableFetchProfile( profileName );
return this;
}
@Override
public SimpleNaturalIdLoadAccess<T> disableFetchProfile(String profileName) {
super.enableFetchProfile( profileName );
return this;
}
}

View File

@ -11,6 +11,7 @@ import java.time.Instant;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@ -631,7 +632,7 @@ public abstract class AbstractQuery<R>
@Override
public int executeUpdate() throws HibernateException {
getSession().checkTransactionNeededForUpdateOperation( "Executing an update/delete query" );
beforeQuery();
final HashSet<String> fetchProfiles = beforeQuery();
boolean success = false;
try {
final int result = doExecuteUpdate();
@ -648,7 +649,7 @@ public abstract class AbstractQuery<R>
throw getSession().getExceptionConverter().convert( e );
}
finally {
afterQuery( success );
afterQuery( success, fetchProfiles );
}
}

View File

@ -15,7 +15,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
@ -41,7 +40,6 @@ import org.hibernate.LockOptions;
import org.hibernate.NonUniqueResultException;
import org.hibernate.ScrollMode;
import org.hibernate.TypeMismatchException;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.graph.GraphSemantic;
@ -419,11 +417,10 @@ public abstract class AbstractSelectionQuery<R>
private FlushMode sessionFlushMode;
private CacheMode sessionCacheMode;
private HashSet<String> fetchProfiles;
@Override
public List<R> list() {
beforeQuery();
final HashSet<String> fetchProfiles = beforeQuery();
boolean success = false;
try {
final List<R> result = doList();
@ -440,16 +437,17 @@ public abstract class AbstractSelectionQuery<R>
throw getSession().getExceptionConverter().convert( he, getQueryOptions().getLockOptions() );
}
finally {
afterQuery( success );
afterQuery( success, fetchProfiles );
}
}
protected void beforeQuery() {
protected HashSet<String> beforeQuery() {
getQueryParameterBindings().validate();
getSession().prepareForQueryExecution(
requiresTxn( getQueryOptions().getLockOptions().findGreatestLockMode() )
);
final SharedSessionContractImplementor session = getSession();
final MutableQueryOptions options = getQueryOptions();
session.prepareForQueryExecution( requiresTxn( options.getLockOptions().findGreatestLockMode() ) );
prepareForExecution();
assert sessionFlushMode == null;
@ -457,47 +455,33 @@ public abstract class AbstractSelectionQuery<R>
final FlushMode effectiveFlushMode = getHibernateFlushMode();
if ( effectiveFlushMode != null ) {
sessionFlushMode = getSession().getHibernateFlushMode();
getSession().setHibernateFlushMode( effectiveFlushMode );
sessionFlushMode = session.getHibernateFlushMode();
session.setHibernateFlushMode( effectiveFlushMode );
}
final CacheMode effectiveCacheMode = getCacheMode();
if ( effectiveCacheMode != null ) {
sessionCacheMode = getSession().getCacheMode();
getSession().setCacheMode( effectiveCacheMode );
sessionCacheMode = session.getCacheMode();
session.setCacheMode( effectiveCacheMode );
}
final LoadQueryInfluencers loadQueryInfluencers = getSession().getLoadQueryInfluencers();
fetchProfiles = loadQueryInfluencers.hasEnabledFetchProfiles()
? new HashSet<>( loadQueryInfluencers.getEnabledFetchProfileNames() )
: null;
final Set<String> disabledFetchProfiles = getQueryOptions().getDisabledFetchProfiles();
if ( disabledFetchProfiles != null ) {
for ( String name: disabledFetchProfiles ) {
loadQueryInfluencers.disableFetchProfile( name );
}
}
final Set<String> enabledFetchProfiles = getQueryOptions().getEnabledFetchProfiles();
if ( enabledFetchProfiles != null ) {
for ( String name: enabledFetchProfiles ) {
loadQueryInfluencers.enableFetchProfile( name );
}
}
return session.getLoadQueryInfluencers()
.adjustFetchProfiles( options.getDisabledFetchProfiles(), options.getEnabledFetchProfiles() );
}
protected abstract void prepareForExecution();
protected void afterQuery(boolean success) {
protected void afterQuery(boolean success, HashSet<String> fetchProfiles) {
getSession().getLoadQueryInfluencers().setEnabledFetchProfileNames( fetchProfiles );
final SharedSessionContractImplementor session = getSession();
session.getLoadQueryInfluencers().setEnabledFetchProfileNames( fetchProfiles );
afterQuery();
if ( !getSession().isTransactionInProgress() ) {
getSession().getJdbcCoordinator().getLogicalConnection().afterTransaction();
if ( !session.isTransactionInProgress() ) {
session.getJdbcCoordinator().getLogicalConnection().afterTransaction();
}
getSession().afterOperation( success );
session.afterOperation( success );
}
protected void afterQuery() {

View File

@ -14,6 +14,7 @@ import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@ -668,7 +669,7 @@ public class QuerySqmImpl<R>
public int executeUpdate() {
verifyUpdate();
getSession().checkTransactionNeededForUpdateOperation( "Executing an update/delete query" );
beforeQuery();
final HashSet<String> fetchProfiles = beforeQuery();
boolean success = false;
try {
final int result = doExecuteUpdate();
@ -685,7 +686,7 @@ public class QuerySqmImpl<R>
throw getSession().getExceptionConverter().convert( e );
}
finally {
afterQuery( success );
afterQuery( success, fetchProfiles );
}
}

View File

@ -19,12 +19,18 @@ public interface Dao {
@Find
Book getBook(String title, String author);
@Find(enabledFetchProfiles="Hello")
Book getBookFetching(String title, String author);
@Find
Book getBook(String title, String isbn, String author);
@Find
List<Book> getBooks(String title);
@Find(enabledFetchProfiles="Hello")
List<Book> getBooksFetching(String title);
@Find
SelectionQuery<Book> createBooksSelectionQuery(String title);