mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-17 16:44:57 +00:00
HHH-12558 - Lazy load EntityLoaders to improve memory usage
This commit is contained in:
parent
7943fe3fc2
commit
60f4645036
@ -6,6 +6,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.boot;
|
package org.hibernate.boot;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
@ -27,9 +30,6 @@
|
|||||||
import org.hibernate.tuple.entity.EntityTuplizer;
|
import org.hibernate.tuple.entity.EntityTuplizer;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The contract for building a {@link org.hibernate.SessionFactory} given a number of options.
|
* The contract for building a {@link org.hibernate.SessionFactory} given a number of options.
|
||||||
*
|
*
|
||||||
@ -303,6 +303,14 @@ SessionFactoryBuilder applyEntityTuplizer(
|
|||||||
*/
|
*/
|
||||||
SessionFactoryBuilder applyBatchFetchStyle(BatchFetchStyle style);
|
SessionFactoryBuilder applyBatchFetchStyle(BatchFetchStyle style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should entity Loaders be generated immediately? Or should the creation
|
||||||
|
* be delayed until first need?
|
||||||
|
*
|
||||||
|
* @see org.hibernate.cfg.AvailableSettings#DELAY_ENTITY_LOADER_CREATIONS
|
||||||
|
*/
|
||||||
|
SessionFactoryBuilder applyDelayedEntityLoaderCreations(boolean delay);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows specifying a default batch-fetch size for all entities and collections
|
* Allows specifying a default batch-fetch size for all entities and collections
|
||||||
* which do not otherwise specify a batch-fetch size.
|
* which do not otherwise specify a batch-fetch size.
|
||||||
|
@ -219,6 +219,12 @@ public SessionFactoryBuilder applyBatchFetchStyle(BatchFetchStyle style) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionFactoryBuilder applyDelayedEntityLoaderCreations(boolean delay) {
|
||||||
|
this.optionsBuilder.applyDelayedEntityLoaderCreations( delay );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionFactoryBuilder applyDefaultBatchFetchSize(int size) {
|
public SessionFactoryBuilder applyDefaultBatchFetchSize(int size) {
|
||||||
this.optionsBuilder.applyDefaultBatchFetchSize( size );
|
this.optionsBuilder.applyDefaultBatchFetchSize( size );
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
import static org.hibernate.cfg.AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATEGY;
|
import static org.hibernate.cfg.AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATEGY;
|
||||||
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
|
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
|
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.DELAY_ENTITY_LOADER_CREATIONS;
|
||||||
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
|
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
|
||||||
import static org.hibernate.cfg.AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
|
import static org.hibernate.cfg.AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
|
||||||
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
||||||
@ -183,6 +184,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||||||
private MultiTableBulkIdStrategy multiTableBulkIdStrategy;
|
private MultiTableBulkIdStrategy multiTableBulkIdStrategy;
|
||||||
private TempTableDdlTransactionHandling tempTableDdlTransactionHandling;
|
private TempTableDdlTransactionHandling tempTableDdlTransactionHandling;
|
||||||
private BatchFetchStyle batchFetchStyle;
|
private BatchFetchStyle batchFetchStyle;
|
||||||
|
private boolean delayBatchFetchLoaderCreations;
|
||||||
private int defaultBatchFetchSize;
|
private int defaultBatchFetchSize;
|
||||||
private Integer maximumFetchDepth;
|
private Integer maximumFetchDepth;
|
||||||
private NullPrecedence defaultNullPrecedence;
|
private NullPrecedence defaultNullPrecedence;
|
||||||
@ -324,6 +326,7 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.batchFetchStyle = BatchFetchStyle.interpret( configurationSettings.get( BATCH_FETCH_STYLE ) );
|
this.batchFetchStyle = BatchFetchStyle.interpret( configurationSettings.get( BATCH_FETCH_STYLE ) );
|
||||||
|
this.delayBatchFetchLoaderCreations = cfgService.getSetting( DELAY_ENTITY_LOADER_CREATIONS, BOOLEAN, true );
|
||||||
this.defaultBatchFetchSize = ConfigurationHelper.getInt( DEFAULT_BATCH_FETCH_SIZE, configurationSettings, -1 );
|
this.defaultBatchFetchSize = ConfigurationHelper.getInt( DEFAULT_BATCH_FETCH_SIZE, configurationSettings, -1 );
|
||||||
this.maximumFetchDepth = ConfigurationHelper.getInteger( MAX_FETCH_DEPTH, configurationSettings );
|
this.maximumFetchDepth = ConfigurationHelper.getInteger( MAX_FETCH_DEPTH, configurationSettings );
|
||||||
final String defaultNullPrecedence = ConfigurationHelper.getString(
|
final String defaultNullPrecedence = ConfigurationHelper.getString(
|
||||||
@ -773,6 +776,11 @@ public BatchFetchStyle getBatchFetchStyle() {
|
|||||||
return batchFetchStyle;
|
return batchFetchStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayBatchFetchLoaderCreationsEnabled() {
|
||||||
|
return delayBatchFetchLoaderCreations;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultBatchFetchSize() {
|
public int getDefaultBatchFetchSize() {
|
||||||
return defaultBatchFetchSize;
|
return defaultBatchFetchSize;
|
||||||
@ -1110,6 +1118,10 @@ public void applyBatchFetchStyle(BatchFetchStyle style) {
|
|||||||
this.batchFetchStyle = style;
|
this.batchFetchStyle = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void applyDelayedEntityLoaderCreations(boolean delay) {
|
||||||
|
this.delayBatchFetchLoaderCreations = delay;
|
||||||
|
}
|
||||||
|
|
||||||
public void applyDefaultBatchFetchSize(int size) {
|
public void applyDefaultBatchFetchSize(int size) {
|
||||||
this.defaultBatchFetchSize = size;
|
this.defaultBatchFetchSize = size;
|
||||||
}
|
}
|
||||||
@ -1300,5 +1312,4 @@ public SessionFactoryOptions buildOptions() {
|
|||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.boot.spi;
|
package org.hibernate.boot.spi;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
@ -28,9 +31,6 @@
|
|||||||
import org.hibernate.tuple.entity.EntityTuplizer;
|
import org.hibernate.tuple.entity.EntityTuplizer;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience base class for custom implementors of SessionFactoryBuilder, using delegation
|
* Convenience base class for custom implementors of SessionFactoryBuilder, using delegation
|
||||||
*
|
*
|
||||||
@ -193,6 +193,12 @@ public T applyBatchFetchStyle(BatchFetchStyle style) {
|
|||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionFactoryBuilder applyDelayedEntityLoaderCreations(boolean delay) {
|
||||||
|
delegate.applyDelayedEntityLoaderCreations( delay );
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T applyDefaultBatchFetchSize(int size) {
|
public T applyDefaultBatchFetchSize(int size) {
|
||||||
delegate.applyDefaultBatchFetchSize( size );
|
delegate.applyDefaultBatchFetchSize( size );
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.hibernate.boot.spi;
|
package org.hibernate.boot.spi;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.ConnectionReleaseMode;
|
import org.hibernate.ConnectionReleaseMode;
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
@ -31,10 +35,6 @@
|
|||||||
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
import org.hibernate.resource.jdbc.spi.StatementInspector;
|
||||||
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience base class for custom implementors of SessionFactoryOptions, using delegation
|
* Convenience base class for custom implementors of SessionFactoryOptions, using delegation
|
||||||
*
|
*
|
||||||
@ -172,6 +172,11 @@ public BatchFetchStyle getBatchFetchStyle() {
|
|||||||
return delegate.getBatchFetchStyle();
|
return delegate.getBatchFetchStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDelayBatchFetchLoaderCreationsEnabled() {
|
||||||
|
return delegate.isDelayBatchFetchLoaderCreationsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDefaultBatchFetchSize() {
|
public int getDefaultBatchFetchSize() {
|
||||||
return delegate.getDefaultBatchFetchSize();
|
return delegate.getDefaultBatchFetchSize();
|
||||||
|
@ -161,6 +161,8 @@ default Supplier<? extends Interceptor> getStatelessInterceptorImplementorSuppli
|
|||||||
|
|
||||||
BatchFetchStyle getBatchFetchStyle();
|
BatchFetchStyle getBatchFetchStyle();
|
||||||
|
|
||||||
|
boolean isDelayBatchFetchLoaderCreationsEnabled();
|
||||||
|
|
||||||
int getDefaultBatchFetchSize();
|
int getDefaultBatchFetchSize();
|
||||||
|
|
||||||
Integer getMaximumFetchDepth();
|
Integer getMaximumFetchDepth();
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
package org.hibernate.cfg;
|
package org.hibernate.cfg;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
@ -1560,6 +1559,20 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||||||
*/
|
*/
|
||||||
String BATCH_FETCH_STYLE = "hibernate.batch_fetch_style";
|
String BATCH_FETCH_STYLE = "hibernate.batch_fetch_style";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls how the individual Loaders for an entity are created.
|
||||||
|
*
|
||||||
|
* When `true` (the default), only the minimal set of Loaders are
|
||||||
|
* created. These include the handling for {@link org.hibernate.LockMode#READ}
|
||||||
|
* and {@link org.hibernate.LockMode#NONE} as well as specialized Loaders for
|
||||||
|
* merge and refresh handling.
|
||||||
|
*
|
||||||
|
* `false` indicates that all loaders should be created up front
|
||||||
|
*
|
||||||
|
* @since 5.3
|
||||||
|
*/
|
||||||
|
String DELAY_ENTITY_LOADER_CREATIONS = "hibernate.loader.delay_entity_loader_creations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction can be rolled back by another thread ("tracking by thread")
|
* A transaction can be rolled back by another thread ("tracking by thread")
|
||||||
* -- not the original application. Examples of this include a JTA
|
* -- not the original application. Examples of this include a JTA
|
||||||
@ -1922,4 +1935,5 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
|||||||
* @since 5.2.17
|
* @since 5.2.17
|
||||||
*/
|
*/
|
||||||
String IN_CLAUSE_PARAMETER_PADDING = "hibernate.query.in_clause_parameter_padding";
|
String IN_CLAUSE_PARAMETER_PADDING = "hibernate.query.in_clause_parameter_padding";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,13 @@
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -4113,7 +4115,12 @@ public final void postInstantiate() throws MappingException {
|
|||||||
protected void doPostInstantiate() {
|
protected void doPostInstantiate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//needed by subclasses to override the createLoader strategy
|
/**
|
||||||
|
* "Needed" by subclasses to override the createLoader strategy
|
||||||
|
*
|
||||||
|
* @deprecated Because there are better patterns for this
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
protected Map getLoaders() {
|
protected Map getLoaders() {
|
||||||
return loaders;
|
return loaders;
|
||||||
}
|
}
|
||||||
@ -4125,9 +4132,17 @@ protected void createLoaders() {
|
|||||||
noneLockLoader = createEntityLoader( LockMode.NONE );
|
noneLockLoader = createEntityLoader( LockMode.NONE );
|
||||||
readLockLoader = createEntityLoader( LockMode.READ );
|
readLockLoader = createEntityLoader( LockMode.READ );
|
||||||
|
|
||||||
final Map loaders = getLoaders();
|
|
||||||
|
|
||||||
// The loaders for the other lock modes are lazily loaded and will later be stored in this map.
|
// The loaders for the other lock modes are lazily loaded and will later be stored in this map,
|
||||||
|
// unless this setting is disabled
|
||||||
|
if ( ! factory.getSessionFactoryOptions().isDelayBatchFetchLoaderCreationsEnabled() ) {
|
||||||
|
for ( LockMode lockMode : EnumSet.complementOf( EnumSet.of( LockMode.NONE, LockMode.READ ) ) ) {
|
||||||
|
loaders.put( lockMode, createEntityLoader( lockMode ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// And finally, create the internal merge and refresh load plans
|
||||||
|
|
||||||
loaders.put(
|
loaders.put(
|
||||||
"merge",
|
"merge",
|
||||||
@ -4147,10 +4162,10 @@ else if ( LockMode.READ == lockMode ) {
|
|||||||
return readLockLoader;
|
return readLockLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
return loaders.computeIfAbsent( lockMode, this::createLazyLoadedEntityLoader );
|
return loaders.computeIfAbsent( lockMode, this::generateDelayedEntityLoader );
|
||||||
}
|
}
|
||||||
|
|
||||||
private UniqueEntityLoader createLazyLoadedEntityLoader(Object lockModeObject) {
|
private UniqueEntityLoader generateDelayedEntityLoader(Object lockModeObject) {
|
||||||
// Unfortunately, the loaders map mixes LockModes and Strings as keys so we need to accept an Object.
|
// Unfortunately, the loaders map mixes LockModes and Strings as keys so we need to accept an Object.
|
||||||
// The cast is safe as we will always call this method with a LockMode.
|
// The cast is safe as we will always call this method with a LockMode.
|
||||||
LockMode lockMode = (LockMode) lockModeObject;
|
LockMode lockMode = (LockMode) lockModeObject;
|
||||||
@ -4159,23 +4174,26 @@ private UniqueEntityLoader createLazyLoadedEntityLoader(Object lockModeObject) {
|
|||||||
case NONE:
|
case NONE:
|
||||||
case READ:
|
case READ:
|
||||||
case OPTIMISTIC:
|
case OPTIMISTIC:
|
||||||
case OPTIMISTIC_FORCE_INCREMENT:
|
case OPTIMISTIC_FORCE_INCREMENT: {
|
||||||
return createEntityLoader( lockMode );
|
return createEntityLoader( lockMode );
|
||||||
|
}
|
||||||
case UPGRADE:
|
case UPGRADE:
|
||||||
case UPGRADE_NOWAIT:
|
case UPGRADE_NOWAIT:
|
||||||
case UPGRADE_SKIPLOCKED:
|
case UPGRADE_SKIPLOCKED:
|
||||||
case FORCE:
|
case FORCE:
|
||||||
case PESSIMISTIC_READ:
|
case PESSIMISTIC_READ:
|
||||||
case PESSIMISTIC_WRITE:
|
case PESSIMISTIC_WRITE:
|
||||||
case PESSIMISTIC_FORCE_INCREMENT:
|
case PESSIMISTIC_FORCE_INCREMENT: {
|
||||||
//TODO: inexact, what we really need to know is: are any outer joins used?
|
//TODO: inexact, what we really need to know is: are any outer joins used?
|
||||||
boolean disableForUpdate = getSubclassTableSpan() > 1 &&
|
boolean disableForUpdate = getSubclassTableSpan() > 1
|
||||||
hasSubclasses() &&
|
&& hasSubclasses()
|
||||||
!getFactory().getDialect().supportsOuterJoinForUpdate();
|
&& !getFactory().getDialect().supportsOuterJoinForUpdate();
|
||||||
|
|
||||||
return disableForUpdate ? readLockLoader : createEntityLoader( lockMode );
|
return disableForUpdate ? readLockLoader : createEntityLoader( lockMode );
|
||||||
default:
|
}
|
||||||
throw new IllegalStateException( String.format( "Lock mode %1$s not supported by entity loaders.", lockMode ) );
|
default: {
|
||||||
|
throw new IllegalStateException( String.format( Locale.ROOT, "Lock mode %1$s not supported by entity loaders.", lockMode ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4258,7 +4276,7 @@ else if ( session.getLoadQueryInfluencers().getInternalFetchProfile() != null &&
|
|||||||
// Next, we consider whether an 'internal' fetch profile has been set.
|
// Next, we consider whether an 'internal' fetch profile has been set.
|
||||||
// This indicates a special fetch profile Hibernate needs applied
|
// This indicates a special fetch profile Hibernate needs applied
|
||||||
// (for its merge loading process e.g.).
|
// (for its merge loading process e.g.).
|
||||||
return (UniqueEntityLoader) getLoaders().get( session.getLoadQueryInfluencers().getInternalFetchProfile() );
|
return loaders.get( session.getLoadQueryInfluencers().getInternalFetchProfile() );
|
||||||
}
|
}
|
||||||
else if ( isAffectedByEnabledFetchProfiles( session ) ) {
|
else if ( isAffectedByEnabledFetchProfiles( session ) ) {
|
||||||
// If the session has associated influencers we need to adjust the
|
// If the session has associated influencers we need to adjust the
|
||||||
@ -4272,7 +4290,7 @@ else if ( lockOptions.getTimeOut() != LockOptions.WAIT_FOREVER ) {
|
|||||||
return createEntityLoader( lockOptions, session.getLoadQueryInfluencers() );
|
return createEntityLoader( lockOptions, session.getLoadQueryInfluencers() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (UniqueEntityLoader) getLoaderByLockMode( lockOptions.getLockMode() );
|
return getLoaderByLockMode( lockOptions.getLockMode() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user