From 95ed41b445c93c407cd21f15d814076ff6b1fa56 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 25 Jan 2016 13:12:08 -0600 Subject: [PATCH] HHH-7572 - Develop API for load-by-multiple-ids --- .../hibernate/MultiIdentifierLoadAccess.java | 3 ++ .../org/hibernate/internal/SessionImpl.java | 38 ++++++++++--------- .../DynamicBatchingEntityLoaderBuilder.java | 34 ++++++++--------- .../entity/AbstractEntityPersister.java | 12 ++++++ .../persister/entity/EntityPersister.java | 13 ++++++- .../persister/entity/MultiLoadOptions.java | 22 +++++++++++ .../GoofyPersisterClassProvider.java | 9 +++++ .../test/legacy/CustomPersister.java | 9 ++++- .../PersisterClassProviderTest.java | 8 ++++ 9 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/entity/MultiLoadOptions.java diff --git a/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java index 3d17f96865..5be84357aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java @@ -38,6 +38,9 @@ public interface MultiIdentifierLoadAccess { * to use a batch sizing strategy defined by the Dialect in use. Any greater-than-one * value here will override that default behavior. If giving an explicit value here, * care should be taken to not exceed the capabilities of of the underlying database. + *

+ * Note that overall a batch-size is considered a hint. How the underlying loading + * mechanism interprets that is completely up to that underlying loading mechanism. * * @param batchSize The batch size * diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 7fcbd79646..d7c93bb88b 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -133,6 +133,7 @@ import org.hibernate.loader.custom.CustomQuery; import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.MultiLoadOptions; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.pretty.MessageHelper; import org.hibernate.procedure.ProcedureCall; @@ -2771,7 +2772,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc } } - private class MultiIdentifierLoadAccessImpl implements MultiIdentifierLoadAccess { + private class MultiIdentifierLoadAccessImpl implements MultiIdentifierLoadAccess, MultiLoadOptions { private final EntityPersister entityPersister; private LockOptions lockOptions; private CacheMode cacheMode; @@ -2782,6 +2783,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc this.entityPersister = entityPersister; } + @Override + public LockOptions getLockOptions() { + return lockOptions; + } + @Override public final MultiIdentifierLoadAccessImpl with(LockOptions lockOptions) { this.lockOptions = lockOptions; @@ -2794,6 +2800,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc return this; } + @Override + public Integer getBatchSize() { + return batchSize; + } + @Override public MultiIdentifierLoadAccess withBatchSize(int batchSize) { if ( batchSize < 1 ) { @@ -2805,6 +2816,11 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc return this; } + @Override + public boolean isSessionCheckingEnabled() { + return sessionCheckingEnabled; + } + @Override public MultiIdentifierLoadAccess enableSessionCheck(boolean enabled) { this.sessionCheckingEnabled = enabled; @@ -2812,6 +2828,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc } @Override + @SuppressWarnings("unchecked") public List multiLoad(K... ids) { CacheMode sessionCacheMode = getCacheMode(); boolean cacheModeChanged = false; @@ -2825,14 +2842,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc } try { - return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad( - (OuterJoinLoadable) entityPersister, - ids, - lockOptions, - batchSize, - sessionCheckingEnabled, - SessionImpl.this - ); + return entityPersister.multiLoad( ids, SessionImpl.this, this ); } finally { if ( cacheModeChanged ) { @@ -2843,6 +2853,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc } @Override + @SuppressWarnings("unchecked") public List multiLoad(List ids) { CacheMode sessionCacheMode = getCacheMode(); boolean cacheModeChanged = false; @@ -2856,14 +2867,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc } try { - return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad( - (OuterJoinLoadable) entityPersister, - ids.toArray( new Serializable[ ids.size() ] ), - lockOptions, - batchSize, - sessionCheckingEnabled, - SessionImpl.this - ); + return entityPersister.multiLoad( ids.toArray( new Serializable[ ids.size() ] ), SessionImpl.this, this ); } finally { if ( cacheModeChanged ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java index d81340c22d..162b0c795e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java @@ -29,6 +29,7 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.loader.spi.AfterLoadAction; +import org.hibernate.persister.entity.MultiLoadOptions; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.pretty.MessageHelper; import org.hibernate.type.Type; @@ -47,26 +48,23 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil public static final DynamicBatchingEntityLoaderBuilder INSTANCE = new DynamicBatchingEntityLoaderBuilder(); @SuppressWarnings("unchecked") - public List multiLoad( + public List multiLoad( OuterJoinLoadable persister, - K[] ids, - LockOptions lockOptions, - Integer explicitBatchSize, - boolean sessionCheckingEnabled, - SessionImplementor session) { - List result = CollectionHelper.arrayList( ids.length ); + Serializable[] ids, SessionImplementor session, + MultiLoadOptions loadOptions) { + List result = CollectionHelper.arrayList( ids.length ); - if ( sessionCheckingEnabled ) { + if ( loadOptions.isSessionCheckingEnabled() ) { // the user requested that we exclude ids corresponding to already managed // entities from the generated load SQL. So here we will iterate all // incoming id values and see whether it corresponds to an existing // entity associated with the PC - if it does we add it to the result // list immediately and remove its id from the group of ids to load. boolean foundAnyManagedEntities = false; - final List nonManagedIds = new ArrayList(); - for ( K id : ids ) { + final List nonManagedIds = new ArrayList(); + for ( Serializable id : ids ) { final EntityKey entityKey = new EntityKey( id, persister ); - final T managedEntity = (T) session.getPersistenceContext().getEntity( entityKey ); + final Object managedEntity = session.getPersistenceContext().getEntity( entityKey ); if ( managedEntity != null ) { foundAnyManagedEntities = true; result.add( managedEntity ); @@ -85,7 +83,7 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil // over-write the ids to be loaded with the collection of // just non-managed ones ids = nonManagedIds.toArray( - (K[]) Array.newInstance( + (Serializable[]) Array.newInstance( ids.getClass().getComponentType(), nonManagedIds.size() ) @@ -94,16 +92,14 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil } } - - if ( lockOptions == null ) { - lockOptions = new LockOptions( LockMode.NONE ); - } + final LockOptions lockOptions = (loadOptions.getLockOptions() == null) + ? new LockOptions( LockMode.NONE ) + : loadOptions.getLockOptions(); int numberOfIdsLeft = ids.length; - final int maxBatchSize; - if ( explicitBatchSize != null && explicitBatchSize > 0 ) { - maxBatchSize = explicitBatchSize; + if ( loadOptions.getBatchSize() != null && loadOptions.getBatchSize() > 0 ) { + maxBatchSize = loadOptions.getBatchSize(); } else { maxBatchSize = session.getFactory().getDialect().getDefaultBatchLoadSizingStrategy().determineOptimalBatchLoadSize( diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index f956dfe761..2c3ea6786a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -79,6 +79,7 @@ import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.FilterHelper; +import org.hibernate.internal.SessionImpl; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.jdbc.Expectation; @@ -86,6 +87,7 @@ import org.hibernate.jdbc.Expectations; import org.hibernate.jdbc.TooManyRowsAffectedException; import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; import org.hibernate.loader.entity.CascadeEntityLoader; +import org.hibernate.loader.entity.DynamicBatchingEntityLoaderBuilder; import org.hibernate.loader.entity.EntityLoader; import org.hibernate.loader.entity.UniqueEntityLoader; import org.hibernate.mapping.Column; @@ -3989,6 +3991,16 @@ public abstract class AbstractEntityPersister return loader.load( id, optionalObject, session, lockOptions ); } + @Override + public List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) { + return DynamicBatchingEntityLoaderBuilder.INSTANCE.multiLoad( + this, + ids, + session, + loadOptions + ); + } + public void registerAffectingFetchProfile(String fetchProfileName) { affectingFetchProfileNames.add( fetchProfileName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 53fe8bfd28..6c2cf70f83 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -7,8 +7,8 @@ package org.hibernate.persister.entity; import java.io.Serializable; +import java.util.List; import java.util.Map; -import java.util.Set; import org.hibernate.EntityMode; import org.hibernate.HibernateException; @@ -364,6 +364,17 @@ public interface EntityPersister extends OptimisticCacheSource, EntityDefinition public Object load(Serializable id, Object optionalObject, LockOptions lockOptions, SessionImplementor session) throws HibernateException; + /** + * Performs a load of multiple entities (of this type) by identifier simultaneously. + * + * @param ids The identifiers to load + * @param session The originating Sesison + * @param loadOptions The options for loading + * + * @return The loaded, matching entities + */ + List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions); + /** * Do a version check (optional operation) */ diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/MultiLoadOptions.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/MultiLoadOptions.java new file mode 100644 index 0000000000..b7040bec67 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/MultiLoadOptions.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.persister.entity; + +import org.hibernate.LockOptions; + +/** + * Encapsulation of the options for performing a load by multiple identifiers. + * + * @author Steve Ebersole + */ +public interface MultiLoadOptions { + boolean isSessionCheckingEnabled(); + + LockOptions getLockOptions(); + + Integer getBatchSize(); +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index 2f7303ddc8..da706d2b35 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -9,7 +9,9 @@ package org.hibernate.test.cfg.persister; import java.io.Serializable; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.Set; @@ -40,6 +42,7 @@ import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.CollectionMetadata; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.MultiLoadOptions; import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.walking.spi.AttributeDefinition; @@ -267,6 +270,12 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return null; } + @Override + public List multiLoad( + Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) { + return Collections.emptyList(); + } + @Override public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) { } diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index aef2119061..d25e564d2c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -7,10 +7,11 @@ package org.hibernate.test.legacy; import java.io.Serializable; +import java.util.Collections; import java.util.Comparator; import java.util.Hashtable; +import java.util.List; import java.util.Map; -import java.util.Set; import org.hibernate.EntityMode; import org.hibernate.HibernateException; @@ -43,6 +44,7 @@ import org.hibernate.internal.util.compare.EqualsHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.MultiLoadOptions; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.walking.spi.AttributeDefinition; import org.hibernate.persister.walking.spi.EntityIdentifierDefinition; @@ -304,6 +306,11 @@ public class CustomPersister implements EntityPersister { return load(id, optionalObject, lockOptions.getLockMode(), session); } + @Override + public List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) { + return Collections.emptyList(); + } + /** * @see EntityPersister#load(Serializable, Object, LockMode, SessionImplementor) */ diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java index 63a27a012a..d24cf53f7c 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java @@ -8,7 +8,9 @@ package org.hibernate.jpa.test.ejb3configuration; import java.io.Serializable; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Map; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceException; @@ -40,6 +42,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.MultiLoadOptions; import org.hibernate.persister.internal.PersisterClassResolverInitiator; import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterCreationContext; @@ -303,6 +306,11 @@ public class PersisterClassProviderTest { return null; } + @Override + public List multiLoad(Serializable[] ids, SessionImplementor session, MultiLoadOptions loadOptions) { + return Collections.emptyList(); + } + @Override public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session) { }