Skip to content

Commit 5ffff1b

Browse files
committed
HHH-18976 Get rid of dynamic array instantiation in MultiEntityLoaderStandard
MultiEntityLoaderStandard is used for arbitrary ID types, including IdClass, making it very problematic to instantiate T[] where T is the ID type: in native images, it requires registering T[] for reflection for every T that can possibly be used as an ID type. Fortunately, MultiEntityLoaderStandard does not, in fact, need concrete-type arrays: Object[] works perfectly well with this implementation, and only the other implementation, MultiIdEntityLoaderArrayParam, actually needs concrete-type arrays. We're truly in a lucky streak, because MultiIdEntityLoaderArrayParam is only used for well-known, basic types such as Integer, which can easily be registered for reflection in native images, and likely will be for other reasons anyway. Some of the dynamic instantiations were originally introduced to fix the following issue: * HHH-17201 -- tested in MultiIdEntityLoadTests The corresponding tests still pass after removing these dynamic array instantiations.
1 parent 62199d0 commit 5ffff1b

File tree

4 files changed

+36
-12
lines changed

4 files changed

+36
-12
lines changed

hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.hibernate.graph.GraphSemantic;
2020
import org.hibernate.graph.RootGraph;
2121
import org.hibernate.graph.spi.RootGraphImplementor;
22-
import org.hibernate.loader.ast.internal.LoaderHelper;
2322
import org.hibernate.persister.entity.EntityPersister;
2423
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
2524

@@ -191,7 +190,7 @@ public <K> List<T> multiLoad(List<K> ids) {
191190
}
192191
else {
193192
return perform( () -> (List<T>) entityPersister.multiLoad(
194-
ids.toArray( LoaderHelper.createTypedArray( ids.get( 0 ).getClass(), ids.size() ) ),
193+
ids.toArray( new Object[0] ),
195194
session,
196195
this
197196
) );

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractMultiIdEntityLoader.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.hibernate.engine.spi.EntityKey;
1212
import org.hibernate.engine.spi.SessionFactoryImplementor;
1313
import org.hibernate.event.spi.EventSource;
14-
import org.hibernate.internal.build.AllowReflection;
1514
import org.hibernate.loader.ast.spi.MultiIdEntityLoader;
1615
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
1716
import org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry;
@@ -22,7 +21,6 @@
2221
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
2322
import org.hibernate.type.descriptor.java.JavaType;
2423

25-
import java.lang.reflect.Array;
2624
import java.util.ArrayList;
2725
import java.util.List;
2826

@@ -40,15 +38,12 @@
4038
public abstract class AbstractMultiIdEntityLoader<T> implements MultiIdEntityLoader<T> {
4139
private final EntityMappingType entityDescriptor;
4240
private final SessionFactoryImplementor sessionFactory;
43-
private final EntityIdentifierMapping identifierMapping;
44-
protected final Object[] idArray;
41+
protected final EntityIdentifierMapping identifierMapping;
4542

46-
@AllowReflection
4743
public AbstractMultiIdEntityLoader(EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) {
4844
this.entityDescriptor = entityDescriptor;
4945
this.sessionFactory = sessionFactory;
5046
identifierMapping = getLoadable().getIdentifierMapping();
51-
idArray = (Object[]) Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 );
5247
}
5348

5449
protected EntityMappingType getEntityDescriptor() {
@@ -303,10 +298,13 @@ else if ( unresolvedIds.size() == ids.length ) {
303298
}
304299
else {
305300
// we need to load only some the ids
306-
return unresolvedIds.toArray( idArray );
301+
return toIdArray( unresolvedIds );
307302
}
308303
}
309304

305+
// Depending on the implementation, a specific subtype of Object[] (e.g. Integer[]) may be needed.
306+
protected abstract Object[] toIdArray(List<Object> ids);
307+
310308
private boolean isIdCoercionEnabled() {
311309
return !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled();
312310
}

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
package org.hibernate.loader.ast.internal;
66

7+
import java.lang.reflect.Array;
8+
import java.util.Arrays;
79
import java.util.List;
810

911
import org.hibernate.LockOptions;
@@ -12,6 +14,7 @@
1214
import org.hibernate.engine.spi.PersistenceContext;
1315
import org.hibernate.engine.spi.SessionFactoryImplementor;
1416
import org.hibernate.event.spi.EventSource;
17+
import org.hibernate.internal.build.AllowReflection;
1518
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
1619
import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader;
1720
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
@@ -44,12 +47,15 @@
4447
public class MultiIdEntityLoaderArrayParam<E> extends AbstractMultiIdEntityLoader<E> implements SqlArrayMultiKeyLoader {
4548
private final JdbcMapping arrayJdbcMapping;
4649
private final JdbcParameter jdbcParameter;
50+
protected final Object[] idArray;
4751

52+
@AllowReflection
4853
public MultiIdEntityLoaderArrayParam(
4954
EntityMappingType entityDescriptor,
5055
SessionFactoryImplementor sessionFactory) {
5156
super( entityDescriptor, sessionFactory );
52-
final Class<?> idClass = idArray.getClass().getComponentType();
57+
final Class<?> idClass = identifierMapping.getJavaType().getJavaTypeClass();
58+
idArray = (Object[]) Array.newInstance( idClass, 0 );
5359
arrayJdbcMapping = resolveArrayJdbcMapping(
5460
getIdentifierMapping().getJdbcMapping(),
5561
idClass,
@@ -117,7 +123,7 @@ protected void loadEntitiesById(
117123

118124
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(1);
119125
jdbcParameterBindings.addBinding( jdbcParameter,
120-
new JdbcParameterBindingImpl( arrayJdbcMapping, idsInBatch.toArray( idArray ) ) );
126+
new JdbcParameterBindingImpl( arrayJdbcMapping, toIdArray( idsInBatch ) ) );
121127

122128
getJdbcSelectExecutor().executeQuery(
123129
getSqlAstTranslatorFactory().buildSelectTranslator( getSessionFactory(), sqlAst )
@@ -165,7 +171,7 @@ protected void loadEntitiesWithUnresolvedIds(
165171
.translate( NO_BINDINGS, QueryOptions.NONE );
166172

167173
final List<E> databaseResults = loadByArrayParameter(
168-
unresolvableIds,
174+
toIdArray( unresolvableIds ),
169175
sqlAst,
170176
jdbcSelectOperation,
171177
jdbcParameter,
@@ -189,4 +195,19 @@ protected void loadEntitiesWithUnresolvedIds(
189195
}
190196
}
191197

198+
@Override
199+
protected Object[] toIdArray(List<Object> ids) {
200+
return ids.toArray( idArray );
201+
}
202+
203+
protected Object[] toIdArray(Object[] ids) {
204+
if ( ids.getClass().equals( idArray.getClass() ) ) {
205+
return ids;
206+
}
207+
else {
208+
Object[] typedIdArray = Arrays.copyOf( idArray, ids.length );
209+
System.arraycopy( ids, 0, typedIdArray, 0, ids.length );
210+
return typedIdArray;
211+
}
212+
}
192213
}

hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,10 @@ protected void loadEntitiesWithUnresolvedIds(
218218
idPosition += batchSize;
219219
}
220220
}
221+
222+
@Override
223+
protected Object[] toIdArray(List<Object> ids) {
224+
// This loader implementation doesn't need arrays to have a specific type, Object[] will do.
225+
return ids.toArray( new Object[0] );
226+
}
221227
}

0 commit comments

Comments
 (0)