diff --git a/hibernate-core/src/main/java/org/hibernate/BatchSize.java b/hibernate-core/src/main/java/org/hibernate/BatchSize.java
new file mode 100644
index 000000000000..1c848dc59d13
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/BatchSize.java
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate;
+
+import jakarta.persistence.FindOption;
+
+import java.util.List;
+
+/**
+ * Specify a batch size, that is, how many entities should be
+ * fetched in each request to the database, for an invocation of
+ * {@link Session#findMultiple(Class, List, FindOption...)}.
+ *
+ *
By default, the batch sizing strategy is determined by the
+ * {@linkplain org.hibernate.dialect.Dialect#getBatchLoadSizingStrategy
+ * SQL dialect}, but
+ *
if some {@code batchSize>1} is specified as an
+ * argument to this method, then that batch size will be used.
+ *
+ *
+ * If an explicit batch size is set manually, care should be taken
+ * to not exceed the capabilities of the underlying database.
+ *
+ * The performance impact of setting a batch size depends on whether
+ * a SQL array may be used to pass the list of identifiers to the
+ * database:
+ *
+ *
for databases which support standard SQL arrays, a smaller
+ * batch size might be extremely inefficient compared to a very
+ * large batch size or no batching at all, but
+ *
on the other hand, for databases with no SQL array type, a
+ * large batch size results in long SQL statements with many JDBC
+ * parameters.
+ *
+ * A batch size is considered a hint. This option has no effect
+ * on {@link Session#find(Class, Object, FindOption...)}.
+ *
+ * @param batchSize The batch size
+ *
+ * @see Session#findMultiple
+ * @see MultiIdentifierLoadAccess#withBatchSize
+ *
+ * @since 7.0
+ *
+ * @author Gavin King
+ */
+public record BatchSize(int batchSize) implements FindOption {
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java
index bab6eb4db9df..657b4fcb3169 100644
--- a/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java
+++ b/hibernate-core/src/main/java/org/hibernate/MultiIdentifierLoadAccess.java
@@ -133,6 +133,18 @@ default MultiIdentifierLoadAccess with(RootGraph graph) {
* If an explicit batch size is set manually, care should be taken
* to not exceed the capabilities of the underlying database.
*
+ * The performance impact of setting a batch size depends on whether
+ * a SQL array may be used to pass the list of identifiers to the
+ * database:
+ *
+ *
for databases which support standard SQL arrays, a smaller
+ * batch size might be extremely inefficient compared to a very
+ * large batch size or no batching at all, but
+ *
on the other hand, for databases with no SQL array type, a
+ * large batch size results in long SQL statements with many JDBC
+ * parameters.
+ *
+ *
* A batch size is considered a hint.
*
* @param batchSize The batch size
diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java
index 7f91fda5e7b2..669e0b16ae34 100644
--- a/hibernate-core/src/main/java/org/hibernate/Session.java
+++ b/hibernate-core/src/main/java/org/hibernate/Session.java
@@ -561,6 +561,18 @@ public interface Session extends SharedSessionContract, EntityManager {
* Every object returned by {@code findMultiple()} is either an unproxied instance of the
* given entity class, or a fully-fetched proxy object.
*
+ * This method accepts {@link BatchSize} as an option, allowing control over the number of
+ * records retrieved in a single database request. The performance impact of setting a batch
+ * size depends on whether a SQL array may be used to pass the list of identifiers to the
+ * database:
+ *
+ *
for databases which {@linkplain org.hibernate.dialect.Dialect#supportsStandardArrays
+ * support standard SQL arrays}, a smaller batch size might be extremely inefficient
+ * compared to a very large batch size or no batching at all, but
+ *
on the other hand, for databases with no SQL array type, a large batch size results
+ * in long SQL statements with many JDBC parameters.
+ *
+ *
* For more advanced cases, use {@link #byMultipleIds(Class)}, which returns an instance of
* {@link MultiIdentifierLoadAccess}.
*
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
index 21fe552bfcf2..6778f27518eb 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -71,7 +71,6 @@
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.CoreMessageLogger;
-import org.hibernate.internal.util.MathHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.ast.spi.MultiKeyLoadSizingStrategy;
@@ -208,6 +207,7 @@
import static org.hibernate.cfg.AvailableSettings.NON_CONTEXTUAL_LOB_CREATION;
import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE;
import static org.hibernate.cfg.AvailableSettings.USE_GET_GENERATED_KEYS;
+import static org.hibernate.internal.util.MathHelper.ceilingPowerOfTwo;
import static org.hibernate.internal.util.StringHelper.splitAtCommas;
import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY;
import static org.hibernate.type.SqlTypes.*;
@@ -4385,21 +4385,13 @@ public MultiKeyLoadSizingStrategy getBatchLoadSizingStrategy() {
return getMultiKeyLoadSizingStrategy();
}
- protected final MultiKeyLoadSizingStrategy STANDARD_MULTI_KEY_LOAD_SIZING_STRATEGY = (numberOfColumns, numberOfKeys, pad) -> {
- numberOfKeys = pad ? MathHelper.ceilingPowerOfTwo( numberOfKeys ) : numberOfKeys;
-
- final long parameterCount = (long) numberOfColumns * numberOfKeys;
- final int limit = getParameterCountLimit();
-
- if ( limit > 0 ) {
- // the Dialect reported a limit - see if the parameter count exceeds the limit
- if ( parameterCount >= limit ) {
- return limit / numberOfColumns;
- }
- }
+ private int calculateBatchSize(int numberOfColumns, int numberOfKeys, boolean padToPowerOfTwo) {
+ final int batchSize = padToPowerOfTwo ? ceilingPowerOfTwo( numberOfKeys ) : numberOfKeys;
+ final int maxBatchSize = getParameterCountLimit() / numberOfColumns;
+ return maxBatchSize > 0 && batchSize > maxBatchSize ? maxBatchSize : batchSize;
+ }
- return numberOfKeys;
- };
+ protected final MultiKeyLoadSizingStrategy STANDARD_MULTI_KEY_LOAD_SIZING_STRATEGY = this::calculateBatchSize;
/**
* Is JDBC statement warning logging enabled by default?
diff --git a/hibernate-core/src/main/java/org/hibernate/exception/AuthException.java b/hibernate-core/src/main/java/org/hibernate/exception/AuthException.java
new file mode 100644
index 000000000000..64637bb7eea6
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/exception/AuthException.java
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+
+/**
+ * A {@link JDBCException} indicating an authentication or authorization failure.
+ *
+ * @since 7.0
+ *
+ * @author Gavin King
+ */
+public class AuthException extends JDBCException {
+ /**
+ * Constructor for AuthException.
+ *
+ * @param root The underlying exception.
+ */
+ public AuthException(String message, SQLException root) {
+ super( message, root );
+ }
+
+ /**
+ * Constructor for AuthException.
+ *
+ * @param message Optional message.
+ * @param root The underlying exception.
+ */
+ public AuthException(String message, SQLException root, String sql) {
+ super( message, root, sql );
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/exception/DataException.java b/hibernate-core/src/main/java/org/hibernate/exception/DataException.java
index c77a9e8b2320..4b7a101798e9 100644
--- a/hibernate-core/src/main/java/org/hibernate/exception/DataException.java
+++ b/hibernate-core/src/main/java/org/hibernate/exception/DataException.java
@@ -9,7 +9,7 @@
import org.hibernate.JDBCException;
/**
- * Extends {@link JDBCException} indicating that evaluation of the
+ * A {@link JDBCException} indicating that evaluation of the
* valid SQL statement against the given data resulted in some
* illegal operation, mismatched types or incorrect cardinality.
*
@@ -17,7 +17,7 @@
*/
public class DataException extends JDBCException {
/**
- * Constructor for JDBCException.
+ * Constructor for DataException.
*
* @param root The underlying exception.
*/
@@ -26,7 +26,7 @@ public DataException(String message, SQLException root) {
}
/**
- * Constructor for JDBCException.
+ * Constructor for DataException.
*
* @param message Optional message.
* @param root The underlying exception.
diff --git a/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java b/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java
index ee52df5fbea1..97afadfcab03 100644
--- a/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java
+++ b/hibernate-core/src/main/java/org/hibernate/exception/internal/SQLStateConversionDelegate.java
@@ -5,11 +5,11 @@
package org.hibernate.exception.internal;
import java.sql.SQLException;
-import java.util.Set;
import org.hibernate.JDBCException;
import org.hibernate.PessimisticLockException;
import org.hibernate.QueryTimeoutException;
+import org.hibernate.exception.AuthException;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
@@ -17,108 +17,83 @@
import org.hibernate.exception.SQLGrammarException;
import org.hibernate.exception.spi.AbstractSQLExceptionConversionDelegate;
import org.hibernate.exception.spi.ConversionContext;
-import org.hibernate.internal.util.JdbcExceptionHelper;
import org.checkerframework.checker.nullness.qual.Nullable;
+import static org.hibernate.internal.util.JdbcExceptionHelper.determineSqlStateClassCode;
+import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode;
+import static org.hibernate.internal.util.JdbcExceptionHelper.extractSqlState;
+
/**
* A {@link org.hibernate.exception.spi.SQLExceptionConverter} implementation which performs conversion based
* on the underlying SQLState. Interpretation of a SQL error based on SQLState is not nearly as accurate as
* using the ErrorCode (which is, however, vendor-specific).
- *
- * SQLState codes are defined by both ANSI SQL specs and X/Open. Some "classes" are shared, others are
- * specific to one or another, yet others are custom vendor classes. Unfortunately I have not been able to
- * find a "blessed" list of X/Open codes. These codes are cobbled together between ANSI SQL spec and error
+ *
+ * @implNote
+ * SQLState codes are defined by both ANSI SQL specs and X/Open. Some "classes" are shared, others are
+ * specific to one or another, yet others are custom vendor classes. Unfortunately I have not been able to
+ * find a "blessed" list of X/Open codes. These codes are cobbled together between ANSI SQL spec and error
* code tables from few vendor's documentation.
*
* @author Steve Ebersole
*/
public class SQLStateConversionDelegate extends AbstractSQLExceptionConversionDelegate {
- private static final Set SQL_GRAMMAR_CATEGORIES = buildGrammarCategories();
- private static Set buildGrammarCategories() {
- return Set.of(
- "07", // "dynamic SQL error"
- "20",
- "2A", // "direct SQL syntax error or access rule violation"
- "37", // "dynamic SQL syntax error or access rule violation"
- "42", // "syntax error or access rule violation"
- "65", // Oracle specific as far as I can tell
- "S0" // MySQL specific as far as I can tell
- );
- }
-
- private static final Set DATA_CATEGORIES = buildDataCategories();
- private static Set buildDataCategories() {
- return Set.of(
- "21", // "cardinality violation"
- "22" // "data exception"
- );
- }
-
- private static final Set INTEGRITY_VIOLATION_CATEGORIES = buildContraintCategories();
- private static Set buildContraintCategories() {
- return Set.of(
- "23", // "integrity constraint violation"
- "27", // "triggered data change violation"
- "44" // "with check option violation"
- );
- }
-
- private static final Set CONNECTION_CATEGORIES = buildConnectionCategories();
- private static Set buildConnectionCategories() {
- return Set.of(
- "08" // "connection exception"
- );
- }
-
public SQLStateConversionDelegate(ConversionContext conversionContext) {
super( conversionContext );
}
@Override
public @Nullable JDBCException convert(SQLException sqlException, String message, String sql) {
- final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
- final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
-
+ final String sqlState = extractSqlState( sqlException );
if ( sqlState != null ) {
- String sqlStateClassCode = JdbcExceptionHelper.determineSqlStateClassCode( sqlState );
-
- if ( sqlStateClassCode != null ) {
- if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
+ switch ( sqlState ) {
+ case "42501":
+ return new AuthException( message, sqlException, sql );
+ case "40001":
+ return new LockAcquisitionException( message, sqlException, sql );
+ case "40XL1", "40XL2":
+ // Derby "A lock could not be obtained within the time requested."
+ return new PessimisticLockException( message, sqlException, sql );
+ case "70100":
+ // MySQL Query execution was interrupted
+ return new QueryTimeoutException( message, sqlException, sql );
+ case "72000":
+ if ( extractErrorCode( sqlException ) == 1013 ) {
+ // Oracle user requested cancel of current operation
+ return new QueryTimeoutException( message, sqlException, sql );
+ }
+ }
+ switch ( determineSqlStateClassCode( sqlState ) ) {
+ case
+ "07", // "dynamic SQL error"
+ "20",
+ "2A", // "direct SQL syntax error or access rule violation"
+ "37", // "dynamic SQL syntax error or access rule violation"
+ "42", // "syntax error or access rule violation"
+ "65", // Oracle specific as far as I can tell
+ "S0": // MySQL specific as far as I can tell
return new SQLGrammarException( message, sqlException, sql );
- }
- else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ case
+ "23", // "integrity constraint violation"
+ "27", // "triggered data change violation"
+ "44": // "with check option violation"
final String constraintName = getConversionContext()
.getViolatedConstraintNameExtractor()
.extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName );
- }
- else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ case
+ "08": // "connection exception"
return new JDBCConnectionException( message, sqlException, sql );
- }
- else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
+ case
+ "21", // "cardinality violation"
+ "22": // "data exception"
return new DataException( message, sqlException, sql );
- }
- }
-
- if ( "40001".equals( sqlState ) ) {
- return new LockAcquisitionException( message, sqlException, sql );
- }
-
- if ( "40XL1".equals( sqlState ) || "40XL2".equals( sqlState )) {
- // Derby "A lock could not be obtained within the time requested."
- return new PessimisticLockException( message, sqlException, sql );
- }
-
- // MySQL Query execution was interrupted
- if ( "70100".equals( sqlState ) ||
- // Oracle user requested cancel of current operation
- ( "72000".equals( sqlState ) && errorCode == 1013 ) ) {
- return new QueryTimeoutException( message, sqlException, sql );
+ case
+ "28": // "authentication failure"
+ return new AuthException( message, sqlException, sql );
}
}
-
return null;
}
}
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 e3dd9532d47c..ae5b295fadb5 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java
@@ -24,6 +24,7 @@
import jakarta.persistence.PessimisticLockScope;
import jakarta.persistence.Timeout;
+import org.hibernate.BatchSize;
import org.hibernate.CacheMode;
import org.hibernate.ConnectionAcquisitionMode;
import org.hibernate.EntityFilterException;
@@ -950,6 +951,7 @@ private MultiIdentifierLoadAccess multiloadAccessWithOptions(Class ent
CacheStoreMode storeMode = getCacheStoreMode();
CacheRetrieveMode retrieveMode = getCacheRetrieveMode();
LockOptions lockOptions = copySessionLockOptions();
+ int batchSize = -1;
for ( FindOption option : options ) {
if ( option instanceof CacheStoreMode cacheStoreMode ) {
storeMode = cacheStoreMode;
@@ -982,8 +984,13 @@ else if ( option instanceof EnabledFetchProfile enabledFetchProfile ) {
else if ( option instanceof ReadOnlyMode ) {
loadAccess.withReadOnly( option == ReadOnlyMode.READ_ONLY );
}
+ else if ( option instanceof BatchSize batchSizeOption ) {
+ batchSize = batchSizeOption.batchSize();
+ }
}
- loadAccess.with( lockOptions ).with( interpretCacheMode( storeMode, retrieveMode ) );
+ loadAccess.with( lockOptions )
+ .with( interpretCacheMode( storeMode, retrieveMode ) )
+ .withBatchSize( batchSize );
return loadAccess;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/JdbcExceptionHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/JdbcExceptionHelper.java
index f02744f6013a..fba9a3d0872d 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/util/JdbcExceptionHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/JdbcExceptionHelper.java
@@ -56,9 +56,6 @@ public static String extractSqlStateClassCode(SQLException sqlException) {
}
public static String determineSqlStateClassCode(String sqlState) {
- if ( sqlState == null || sqlState.length() < 2 ) {
- return sqlState;
- }
- return sqlState.substring( 0, 2 );
+ return sqlState == null || sqlState.length() < 2 ? sqlState : sqlState.substring( 0, 2 );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java
index 9090068dc9c3..91211ce1c2ba 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java
@@ -262,7 +262,7 @@ public static Set makeCopy(Set source) {
return copy;
}
- public static boolean isEmpty(Collection collection) {
+ public static boolean isEmpty(Collection> collection) {
return collection == null || collection.isEmpty();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java
index 38501fcae14f..3758d3d993ec 100644
--- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java
+++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java
@@ -4,15 +4,33 @@
*/
package org.hibernate.loader.ast.internal;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.hibernate.LockMode;
+import org.hibernate.LockOptions;
+import org.hibernate.engine.jdbc.spi.JdbcServices;
+import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.spi.EventSource;
+import org.hibernate.event.spi.LoadEvent;
+import org.hibernate.event.spi.LoadEventListener;
import org.hibernate.loader.ast.spi.MultiIdEntityLoader;
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
+import org.hibernate.sql.ast.SqlAstTranslatorFactory;
+import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
+import org.hibernate.type.descriptor.java.JavaType;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
import java.util.List;
+import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
+import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
+import static org.hibernate.loader.ast.internal.CacheEntityLoaderHelper.loadFromSessionCacheStatic;
+import static org.hibernate.loader.ast.internal.LoaderHelper.getReadOnlyFromLoadQueryInfluencers;
+import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
+
/**
* Base support for {@link MultiIdEntityLoader} implementations.
*
@@ -22,11 +40,13 @@ public abstract class AbstractMultiIdEntityLoader implements MultiIdEntityLoa
private final EntityMappingType entityDescriptor;
private final SessionFactoryImplementor sessionFactory;
private final EntityIdentifierMapping identifierMapping;
+ protected final Object[] idArray;
public AbstractMultiIdEntityLoader(EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) {
this.entityDescriptor = entityDescriptor;
this.sessionFactory = sessionFactory;
identifierMapping = getLoadable().getIdentifierMapping();
+ idArray = (Object[]) Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 );
}
protected EntityMappingType getEntityDescriptor() {
@@ -41,6 +61,18 @@ public EntityIdentifierMapping getIdentifierMapping() {
return identifierMapping;
}
+ protected JdbcServices getJdbcServices() {
+ return getSessionFactory().getJdbcServices();
+ }
+
+ protected SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
+ return getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
+ }
+
+ protected JdbcSelectExecutor getJdbcSelectExecutor() {
+ return getJdbcServices().getJdbcSelectExecutor();
+ }
+
@Override
public EntityMappingType getLoadable() {
return getEntityDescriptor();
@@ -49,16 +81,296 @@ public EntityMappingType getLoadable() {
@Override
public final List load(K[] ids, MultiIdLoadOptions loadOptions, EventSource session) {
assert ids != null;
- if ( loadOptions.isOrderReturnEnabled() ) {
- return performOrderedMultiLoad( ids, loadOptions, session );
+ return loadOptions.isOrderReturnEnabled()
+ ? performOrderedMultiLoad( ids, loadOptions, session )
+ : performUnorderedMultiLoad( ids, loadOptions, session );
+ }
+
+ private List performUnorderedMultiLoad(
+ Object[] ids,
+ MultiIdLoadOptions loadOptions,
+ EventSource session) {
+ assert !loadOptions.isOrderReturnEnabled();
+ assert ids != null;
+ if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
+ MULTI_KEY_LOAD_LOGGER.tracef( "#performUnorderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() );
+ }
+ return unorderedMultiLoad( ids, loadOptions, lockOptions( loadOptions ), session );
+ }
+
+ protected List performOrderedMultiLoad(
+ Object[] ids,
+ MultiIdLoadOptions loadOptions,
+ EventSource session) {
+ assert loadOptions.isOrderReturnEnabled();
+ assert ids != null;
+ if ( MULTI_KEY_LOAD_LOGGER.isTraceEnabled() ) {
+ MULTI_KEY_LOAD_LOGGER.tracef( "#performOrderedMultiLoad(`%s`, ..)", getLoadable().getEntityName() );
+ }
+ return orderedMultiLoad( ids, loadOptions, lockOptions( loadOptions ), session );
+ }
+
+ private List orderedMultiLoad(
+ Object[] ids,
+ MultiIdLoadOptions loadOptions,
+ LockOptions lockOptions,
+ EventSource session) {
+ final boolean idCoercionEnabled = isIdCoercionEnabled();
+ final JavaType> idType = getLoadable().getIdentifierMapping().getJavaType();
+
+ final int maxBatchSize = maxBatchSize( ids, loadOptions );
+
+ final List