From 3741a627fdfc1893aa5af3cbc1f47e7862af884c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 2 Jan 2025 13:32:18 +0100 Subject: [PATCH 1/6] HHH-18976 Forbid any future new uses of Array.newInstance --- .../collection/spi/PersistentArrayHolder.java | 2 ++ .../org/hibernate/dialect/JsonHelper.java | 2 ++ .../dialect/function/array/DdlTypeHelper.java | 2 ++ ...yViaElementArgumentReturnTypeResolver.java | 2 ++ .../internal/EventListenerGroupImpl.java | 2 ++ .../internal/EventListenerRegistryImpl.java | 2 ++ .../internal/build/AllowReflection.java | 18 +++++++++++++++++ .../util/collections/ArrayHelper.java | 4 ++++ .../event/internal/CallbackRegistryImpl.java | 2 ++ .../AbstractCollectionBatchLoader.java | 2 ++ .../internal/AbstractMultiIdEntityLoader.java | 2 ++ .../CollectionBatchLoaderArrayParam.java | 4 ++++ .../internal/EntityBatchLoaderArrayParam.java | 3 +++ .../loader/ast/internal/LoaderHelper.java | 2 ++ .../results/internal/StandardRowReader.java | 2 ++ .../java/org/hibernate/type/ArrayType.java | 2 ++ .../converter/internal/ArrayConverter.java | 2 ++ .../internal/CollectionConverter.java | 2 ++ .../java/AbstractArrayJavaType.java | 2 ++ .../type/descriptor/java/ArrayJavaType.java | 3 +++ .../descriptor/java/ArrayMutabilityPlan.java | 3 +++ .../java/BooleanPrimitiveArrayJavaType.java | 2 ++ .../java/DoublePrimitiveArrayJavaType.java | 2 ++ .../java/FloatPrimitiveArrayJavaType.java | 2 ++ .../java/IntegerPrimitiveArrayJavaType.java | 2 ++ .../java/LongPrimitiveArrayJavaType.java | 2 ++ .../java/ShortPrimitiveArrayJavaType.java | 2 ++ .../java/spi/BasicCollectionJavaType.java | 2 ++ .../type/descriptor/jdbc/ArrayJdbcType.java | 2 ++ .../type/format/jaxb/JaxbXmlFormatMapper.java | 2 ++ .../src/main/groovy/local.code-quality.gradle | 6 +++++- rules/forbidden-apis.txt | 20 +++++++++++++++++++ 32 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/internal/build/AllowReflection.java create mode 100644 rules/forbidden-apis.txt diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentArrayHolder.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentArrayHolder.java index 426ca5b88ddb..755cf16840c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentArrayHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentArrayHolder.java @@ -18,6 +18,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.type.Type; @@ -34,6 +35,7 @@ * @author Gavin King */ @Incubating +@AllowReflection // We need the ability to create arrays of the same type as in the model. public class PersistentArrayHolder extends AbstractPersistentCollection { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( PersistentArrayHolder.class ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java index 76594c74c14e..ce50d1b6f587 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java @@ -18,6 +18,7 @@ import java.util.Objects; import org.hibernate.Internal; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.CharSequenceHelper; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.metamodel.mapping.EmbeddableMappingType; @@ -1612,6 +1613,7 @@ public Object[] toArray() { } @Override + @AllowReflection // We need the ability to create arrays of requested types dynamically. public T[] toArray(T[] a) { //noinspection unchecked final T[] r = a.length >= size diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java index c4beb0c99c3e..154bd16f7a1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/DdlTypeHelper.java @@ -10,6 +10,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.Size; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.SqlTypedMapping; import org.hibernate.metamodel.model.domain.DomainType; @@ -24,6 +25,7 @@ public class DdlTypeHelper { @SuppressWarnings("unchecked") + @AllowReflection public static BasicType resolveArrayType(DomainType elementType, TypeConfiguration typeConfiguration) { @SuppressWarnings("unchecked") final BasicPluralJavaType arrayJavaType = (BasicPluralJavaType) typeConfiguration.getJavaTypeRegistry() .getDescriptor( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java index 97a5228b6c69..10605215124e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/array/JsonArrayViaElementArgumentReturnTypeResolver.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.function.Supplier; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.model.domain.DomainType; @@ -78,6 +79,7 @@ public BasicValuedMapping resolveFunctionReturnType( return null; } + @AllowReflection public static BasicType resolveJsonArrayType(DomainType elementType, TypeConfiguration typeConfiguration) { final Class arrayClass = Array.newInstance( elementType.getBindableJavaType(), 0 ).getClass(); @SuppressWarnings("unchecked") diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java index 840516bba6e1..3d9081ad082b 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerGroupImpl.java @@ -22,6 +22,7 @@ import org.hibernate.event.service.spi.EventListenerRegistrationException; import org.hibernate.event.service.spi.JpaBootstrapSensitive; import org.hibernate.event.spi.EventType; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; @@ -350,6 +351,7 @@ private void handleListenerAddition(T listener, Consumer additionHandler) { } @SuppressWarnings("unchecked") + @AllowReflection // Possible array types are registered in org.hibernate.graalvm.internal.StaticClassLists.typesNeedingArrayCopy private T[] createListenerArrayForWrite(int len) { return (T[]) Array.newInstance( eventType.baseListenerInterface(), len ); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java index 95111991da73..0d1247ac9a2a 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/event/service/internal/EventListenerRegistryImpl.java @@ -38,6 +38,7 @@ import org.hibernate.event.service.spi.EventListenerRegistrationException; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.jpa.event.spi.CallbackRegistry; import static org.hibernate.event.spi.EventType.AUTO_FLUSH; @@ -122,6 +123,7 @@ public final void setListeners(EventType type, Class... list } @SafeVarargs + @AllowReflection // Possible array types are registered in org.hibernate.graalvm.internal.StaticClassLists.typesNeedingArrayCopy private T[] resolveListenerInstances(EventType type, Class... listenerClasses) { @SuppressWarnings("unchecked") T[] listeners = (T[]) Array.newInstance( type.baseListenerInterface(), listenerClasses.length ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/build/AllowReflection.java b/hibernate-core/src/main/java/org/hibernate/internal/build/AllowReflection.java new file mode 100644 index 000000000000..456cd4574c09 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/internal/build/AllowReflection.java @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.internal.build; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +@Retention( RetentionPolicy.CLASS ) +@Target({ TYPE, METHOD, CONSTRUCTOR }) +public @interface AllowReflection { +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java index 79c115b981f6..556792813a32 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java @@ -18,6 +18,7 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.type.Type; public final class ArrayHelper { @@ -59,6 +60,7 @@ public static int indexOf(Object[] array, int end, Object object) { } @SuppressWarnings("unchecked") + @AllowReflection public static T[] filledArray(T value, Class valueJavaType, int size) { final T[] array = (T[]) Array.newInstance( valueJavaType, size ); Arrays.fill( array, value ); @@ -202,6 +204,7 @@ public static int[] join(int[] x, int[] y) { } @SuppressWarnings("unchecked") + @AllowReflection public static T[] join(T[] x, T... y) { T[] result = (T[]) Array.newInstance( x.getClass().getComponentType(), x.length + y.length ); System.arraycopy( x, 0, result, 0, x.length ); @@ -520,6 +523,7 @@ public static void forEach(T[] array, Consumer consumer) { } @SuppressWarnings("unchecked") + @AllowReflection public static T[] newInstance(Class elementType, int length) { return (T[]) Array.newInstance( elementType, length ); } diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbackRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbackRegistryImpl.java index 5c52ad61fda9..c65ee9116eaf 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbackRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/event/internal/CallbackRegistryImpl.java @@ -9,6 +9,7 @@ import jakarta.persistence.PersistenceException; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.MapBackedClassValue; import org.hibernate.internal.util.collections.ReadOnlyMap; @@ -168,6 +169,7 @@ public static class Builder { private final Map, Callback[]> postUpdates = new HashMap<>(); private final Map, Callback[]> postLoads = new HashMap<>(); + @AllowReflection public void registerCallbacks(Class entityClass, Callback[] callbacks) { if ( callbacks != null ) { for ( Callback callback : callbacks ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractCollectionBatchLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractCollectionBatchLoader.java index b19ef0a592f0..7ac72a5222b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractCollectionBatchLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractCollectionBatchLoader.java @@ -11,6 +11,7 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.ast.spi.CollectionBatchLoader; import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping; @@ -126,6 +127,7 @@ protected void finishInitializingKey(Object key, SharedSessionContractImplemento } + @AllowReflection Object[] resolveKeysToInitialize(Object keyBeingLoaded, SharedSessionContractImplementor session) { final int length = getDomainBatchSize(); final Object[] keysToInitialize = (Object[]) Array.newInstance( 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 6d70c1076e4f..1e5e8951e1d2 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 @@ -11,6 +11,7 @@ import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.EventSource; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry; @@ -42,6 +43,7 @@ public abstract class AbstractMultiIdEntityLoader implements MultiIdEntityLoa private final EntityIdentifierMapping identifierMapping; protected final Object[] idArray; + @AllowReflection public AbstractMultiIdEntityLoader(EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { this.entityDescriptor = entityDescriptor; this.sessionFactory = sessionFactory; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java index c4b543734cfa..029dcd38671e 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java @@ -13,6 +13,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.ast.spi.CollectionBatchLoader; import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; @@ -51,6 +52,7 @@ public class CollectionBatchLoaderArrayParam private final SelectStatement sqlSelect; private final JdbcOperationQuerySelect jdbcSelectOperation; + @AllowReflection public CollectionBatchLoaderArrayParam( int domainBatchSize, LoadQueryInfluencers loadQueryInfluencers, @@ -115,6 +117,7 @@ public PersistentCollection load(Object keyBeingLoaded, SharedSessionContract } + @AllowReflection private PersistentCollection loadEmbeddable( Object keyBeingLoaded, SharedSessionContractImplementor session, @@ -216,6 +219,7 @@ void finishInitializingKeys(Object[] keys, SharedSessionContractImplementor sess } @Override + @AllowReflection Object[] resolveKeysToInitialize(Object keyBeingLoaded, SharedSessionContractImplementor session) { final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor(); if( keyDescriptor.isEmbedded()){ diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java index 08cf5fd13bf5..3a5213eb19fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java @@ -11,6 +11,7 @@ import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; @@ -58,6 +59,7 @@ public class EntityBatchLoaderArrayParam * {@link EntityIdentifierMapping} is not available at that time. On first use, we know we * have it available */ + @AllowReflection public EntityBatchLoaderArrayParam( int domainBatchSize, EntityMappingType entityDescriptor, @@ -106,6 +108,7 @@ public int getDomainBatchSize() { return domainBatchSize; } + @AllowReflection protected Object[] resolveIdsToInitialize(Object pkValue, SharedSessionContractImplementor session) { //TODO: should this really be different to EntityBatchLoaderInPredicate impl? final Class idType = identifierMapping.getJavaType().getJavaTypeClass(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java index 885b46e10def..3ad14036c4e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java @@ -21,6 +21,7 @@ import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.event.spi.EventSource; import org.hibernate.event.monitor.spi.DiagnosticEvent; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.LoaderLogging; import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -202,6 +203,7 @@ public static K[] normalizeKeys( * @param elementClass The type of the array elements. See {@link Class#getComponentType()} * @param length The length to which the array should be created. This is usually zero for Hibernate uses */ + @AllowReflection public static X[] createTypedArray(Class elementClass, @SuppressWarnings("SameParameterValue") int length) { //noinspection unchecked return (X[]) Array.newInstance( elementClass, length ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java index 1e53ddbffe4a..a34b24fe6c4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java @@ -9,6 +9,7 @@ import java.util.List; import org.hibernate.engine.spi.EntityKey; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; @@ -130,6 +131,7 @@ public boolean hasCollectionInitializers() { } @Override + @AllowReflection public T readRow(RowProcessingState rowProcessingState) { coordinateInitializers( rowProcessingState ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java b/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java index 28083585deb3..68c570712195 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java @@ -16,6 +16,7 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.CollectionClassification; import org.hibernate.persister.collection.CollectionPersister; @@ -26,6 +27,7 @@ * A type for persistent arrays. * @author Gavin King */ +@AllowReflection public class ArrayType extends CollectionType { private final Class elementClass; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java index fa228e05adaf..520b46560f52 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/ArrayConverter.java @@ -6,6 +6,7 @@ import java.lang.reflect.Array; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.JavaType; @@ -18,6 +19,7 @@ * @param the unconverted array type * @param the converted array type */ +@AllowReflection public class ArrayConverter implements BasicValueConverter { private final BasicValueConverter elementConverter; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/CollectionConverter.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/CollectionConverter.java index 6e9c8bcc9938..c997d216b77a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/CollectionConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/converter/internal/CollectionConverter.java @@ -7,6 +7,7 @@ import java.lang.reflect.Array; import java.util.Collection; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType; @@ -43,6 +44,7 @@ public X toDomainValue(Y relationalForm) { } @Override + @AllowReflection public Y toRelationalValue(X domainForm) { if ( domainForm == null ) { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java index c359ba704b01..333f2cce40a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/AbstractArrayJavaType.java @@ -8,6 +8,7 @@ import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; import org.hibernate.type.descriptor.converter.internal.ArrayConverter; import org.hibernate.type.BasicArrayType; @@ -20,6 +21,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; +@AllowReflection public abstract class AbstractArrayJavaType extends AbstractClassJavaType implements BasicPluralJavaType { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java index e129da661be1..7fb49c195ab3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayJavaType.java @@ -14,6 +14,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; import org.hibernate.type.BasicPluralType; @@ -29,6 +30,7 @@ * @author Christian Beikov * @author Jordan Gigov */ +@AllowReflection public class ArrayJavaType extends AbstractArrayJavaType { public ArrayJavaType(BasicType baseDescriptor) { @@ -376,6 +378,7 @@ private T[] fromBytes(byte[] bytes) { } } + @AllowReflection private static class ArrayMutabilityPlan implements MutabilityPlan { private final Class componentClass; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java index db71278e10a7..892aaca02963 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java @@ -3,6 +3,8 @@ * Copyright Red Hat Inc. and Hibernate Authors */ package org.hibernate.type.descriptor.java; +import org.hibernate.internal.build.AllowReflection; + import java.lang.reflect.Array; /** @@ -15,6 +17,7 @@ public class ArrayMutabilityPlan extends MutableMutabilityPlan { public static final ArrayMutabilityPlan INSTANCE = new ArrayMutabilityPlan(); @SuppressWarnings({ "unchecked", "SuspiciousSystemArraycopy" }) + @AllowReflection public T deepCopyNotNull(T value) { if ( ! value.getClass().isArray() ) { // ugh! cannot find a way to properly define the type signature here diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java index 2d6b4e07f889..a7ad513fdc57 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java @@ -16,6 +16,7 @@ import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +25,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class BooleanPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final BooleanPrimitiveArrayJavaType INSTANCE = new BooleanPrimitiveArrayJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java index c598d009dbf6..f29c53fe8241 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java @@ -16,6 +16,7 @@ import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +25,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class DoublePrimitiveArrayJavaType extends AbstractArrayJavaType { public static final DoublePrimitiveArrayJavaType INSTANCE = new DoublePrimitiveArrayJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java index 442c09c387f2..db097bc95599 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java @@ -16,6 +16,7 @@ import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +25,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class FloatPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final FloatPrimitiveArrayJavaType INSTANCE = new FloatPrimitiveArrayJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java index 60edc5ebe8f4..3dc831ee2b05 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java @@ -16,6 +16,7 @@ import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +25,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class IntegerPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final IntegerPrimitiveArrayJavaType INSTANCE = new IntegerPrimitiveArrayJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java index d4423f9c0f4a..5369978dca61 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java @@ -16,6 +16,7 @@ import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +25,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class LongPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final LongPrimitiveArrayJavaType INSTANCE = new LongPrimitiveArrayJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java index d0bf0b6731bc..981cfd87df0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java @@ -16,6 +16,7 @@ import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +25,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class ShortPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final ShortPrimitiveArrayJavaType INSTANCE = new ShortPrimitiveArrayJavaType(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java index 88e32b23bd41..cbdef1177c54 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/BasicCollectionJavaType.java @@ -21,6 +21,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.CollectionClassification; @@ -46,6 +47,7 @@ * @author Christian Beikov */ @Incubating +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class BasicCollectionJavaType, E> extends AbstractJavaType implements BasicPluralJavaType { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java index 3bf37b59fb28..7581cf78625b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java @@ -17,6 +17,7 @@ import org.hibernate.dialect.StructHelper; import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.type.BasicPluralType; import org.hibernate.type.descriptor.ValueBinder; @@ -40,6 +41,7 @@ * @author Christian Beikov * @author Jordan Gigov */ +@AllowReflection // See https://hibernate.atlassian.net/browse/HHH-16809 public class ArrayJdbcType implements JdbcType { private final JdbcType elementJdbcType; diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java index cb567855cdcb..6a5becf99558 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java @@ -20,6 +20,7 @@ import jakarta.xml.bind.annotation.XmlElement; import org.hibernate.dialect.XmlHelper; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; @@ -70,6 +71,7 @@ public JaxbXmlFormatMapper(boolean legacyFormat) { } @Override + @AllowReflection public T fromString(CharSequence charSequence, JavaType javaType, WrapperOptions wrapperOptions) { if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) { return (T) charSequence.toString(); diff --git a/local-build-plugins/src/main/groovy/local.code-quality.gradle b/local-build-plugins/src/main/groovy/local.code-quality.gradle index 66308b2978e6..4b41bb60a14d 100644 --- a/local-build-plugins/src/main/groovy/local.code-quality.gradle +++ b/local-build-plugins/src/main/groovy/local.code-quality.gradle @@ -123,10 +123,14 @@ tasks.forbiddenApisMain { //bundledSignatures += ["jdk-system-out", "jdk-non-portable", "jdk-unsafe-${jdkVersions.baseline}"] bundledSignatures += ["jdk-system-out", "jdk-non-portable"] + signaturesFiles += rootProject.files('rules/forbidden-apis.txt') + ignoreSignaturesOfMissingClasses = true + suppressAnnotations += [ "org.hibernate.internal.build.AllowSysOut", "org.hibernate.internal.build.AllowPrintStacktrace", - "org.hibernate.internal.build.AllowNonPortable" + "org.hibernate.internal.build.AllowNonPortable", + "org.hibernate.internal.build.AllowReflection" ] } diff --git a/rules/forbidden-apis.txt b/rules/forbidden-apis.txt new file mode 100644 index 000000000000..01007e7b26c1 --- /dev/null +++ b/rules/forbidden-apis.txt @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# Copyright Red Hat Inc. and Hibernate Authors + +# This file is a list of signatures to feed into Forbidden-API. +# It defines classes/methods to be avoided. +# See here for the syntax of this file: https://github.com/policeman-tools/forbidden-apis/wiki/SignaturesSyntax + +################################################################################################################ +# Reflection-related +@defaultMessage Use 'new Object[]' instead if possible. This forbidden method requires reflection and may not work in natively compiled applications. If you really must use this forbidden method, annotate the calling method with @AllowReflection. + +java.lang.reflect.Array#newInstance(java.lang.Class, int) +java.lang.reflect.Array#newInstance(java.lang.Class, int[]) +org.hibernate.internal.util.collections.ArrayHelper#newInstance(java.lang.Class, int) +org.hibernate.internal.util.collections.ArrayHelper#filledArray(java.lang.Object, java.lang.Class, int) +org.hibernate.internal.util.collections.ArrayHelper#join(java.lang.Object[], java.lang.Object[]) + +################################################################################################################ +# Misc -- put things here as a last resort, but if possible prefer adding a category above with an actionable message. +@defaultMessage Should not be used. \ No newline at end of file From c50c8be57395b2a4714358b0e5a32c36bf5dd35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Thu, 2 Jan 2025 14:47:31 +0100 Subject: [PATCH 2/6] HHH-18976 Deprecate ArrayHelper#newInstance in favor of the standard Array#newInstance --- .../org/hibernate/internal/util/collections/ArrayHelper.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java index 556792813a32..7b7e5a940441 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java @@ -522,6 +522,10 @@ public static void forEach(T[] array, Consumer consumer) { } } + /** + * @deprecated Use {@link Array#newInstance(Class, int)} instead. + */ + @Deprecated @SuppressWarnings("unchecked") @AllowReflection public static T[] newInstance(Class elementType, int length) { From b0fcfab2749ad154e3a892e5325c8630ecc808ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 3 Jan 2025 13:46:30 +0100 Subject: [PATCH 3/6] HHH-18976 Use a clone-based implementation for all array mutability plans It's more consistent, and happens to get rid of ArrayMutabilityPlan, which involved an unnecessary use of Array.newInstance. I've also seen claims that clone() performs better than Array.newInstance() due to not having to zero-out the allocated memory, but I doubt that's relevant here. --- .../descriptor/java/ArrayMutabilityPlan.java | 5 ++++ .../java/BooleanPrimitiveArrayJavaType.java | 25 +++--------------- .../descriptor/java/ByteArrayJavaType.java | 2 +- .../java/CharacterArrayJavaType.java | 2 +- .../java/DoublePrimitiveArrayJavaType.java | 25 +++--------------- .../java/FloatPrimitiveArrayJavaType.java | 25 +++--------------- .../ImmutableObjectArrayMutabilityPlan.java | 26 +++++++++++++++++++ .../java/IntegerPrimitiveArrayJavaType.java | 25 +++--------------- .../java/LongPrimitiveArrayJavaType.java | 25 +++--------------- .../java/PrimitiveByteArrayJavaType.java | 10 +++++-- .../java/PrimitiveCharacterArrayJavaType.java | 9 ++++++- .../java/ShortPrimitiveArrayJavaType.java | 25 +++--------------- 12 files changed, 67 insertions(+), 137 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ImmutableObjectArrayMutabilityPlan.java diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java index 892aaca02963..de058dfbb2e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java @@ -12,7 +12,12 @@ * are immutable, a shallow copy is enough. * * @author Steve Ebersole + * + * @deprecated Use {@link ImmutableObjectArrayMutabilityPlan#get()} for object arrays, + * or implement a dedicated mutability plan for primitive arrays + * (see for example {@link ShortPrimitiveArrayJavaType}'s mutability plan). */ +@Deprecated public class ArrayMutabilityPlan extends MutableMutabilityPlan { public static final ArrayMutabilityPlan INSTANCE = new ArrayMutabilityPlan(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java index a7ad513fdc57..44c59dec3324 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java @@ -4,7 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.io.Serializable; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.ArrayList; @@ -13,7 +12,6 @@ import java.util.List; import org.hibernate.HibernateException; -import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; import org.hibernate.internal.build.AllowReflection; @@ -185,27 +183,10 @@ else if ( value instanceof Collection collection ) { throw unknownWrap( value.getClass() ); } - private static class ArrayMutabilityPlan implements MutabilityPlan { - - @Override - public boolean isMutable() { - return true; - } - + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { @Override - public boolean[] deepCopy(boolean[] value) { - return value == null ? null : value.clone(); + protected boolean[] deepCopyNotNull(boolean[] value) { + return value.clone(); } - - @Override - public Serializable disassemble(boolean[] value, SharedSessionContract session) { - return deepCopy( value ); - } - - @Override - public boolean[] assemble(Serializable cached, SharedSessionContract session) { - return deepCopy( (boolean[]) cached ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java index 8c2cef146ed7..45d97ee7f112 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteArrayJavaType.java @@ -30,7 +30,7 @@ public class ByteArrayJavaType extends AbstractClassJavaType { @SuppressWarnings("unchecked") public ByteArrayJavaType() { - super( Byte[].class, ArrayMutabilityPlan.INSTANCE, IncomparableComparator.INSTANCE ); + super( Byte[].class, ImmutableObjectArrayMutabilityPlan.get(), IncomparableComparator.INSTANCE ); } @Override public boolean areEqual(Byte[] one, Byte[] another) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java index ecdea5ef9da4..f21d020b65fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterArrayJavaType.java @@ -29,7 +29,7 @@ public class CharacterArrayJavaType extends AbstractClassJavaType { @SuppressWarnings("unchecked") public CharacterArrayJavaType() { - super( Character[].class, ArrayMutabilityPlan.INSTANCE, IncomparableComparator.INSTANCE ); + super( Character[].class, ImmutableObjectArrayMutabilityPlan.get(), IncomparableComparator.INSTANCE ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java index f29c53fe8241..e0418b58031b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java @@ -4,7 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.io.Serializable; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.ArrayList; @@ -13,7 +12,6 @@ import java.util.List; import org.hibernate.HibernateException; -import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; import org.hibernate.internal.build.AllowReflection; @@ -185,27 +183,10 @@ else if ( value instanceof Collection collection ) { throw unknownWrap( value.getClass() ); } - private static class ArrayMutabilityPlan implements MutabilityPlan { - - @Override - public boolean isMutable() { - return true; - } - + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { @Override - public double[] deepCopy(double[] value) { - return value == null ? null : value.clone(); + protected double[] deepCopyNotNull(double[] value) { + return value.clone(); } - - @Override - public Serializable disassemble(double[] value, SharedSessionContract session) { - return deepCopy( value ); - } - - @Override - public double[] assemble(Serializable cached, SharedSessionContract session) { - return deepCopy( (double[]) cached ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java index db097bc95599..457b65c3bcf8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java @@ -4,7 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.io.Serializable; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.ArrayList; @@ -13,7 +12,6 @@ import java.util.List; import org.hibernate.HibernateException; -import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; import org.hibernate.internal.build.AllowReflection; @@ -185,27 +183,10 @@ else if ( value instanceof Collection collection ) { throw unknownWrap( value.getClass() ); } - private static class ArrayMutabilityPlan implements MutabilityPlan { - - @Override - public boolean isMutable() { - return true; - } - + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { @Override - public float[] deepCopy(float[] value) { - return value == null ? null : value.clone(); + protected float[] deepCopyNotNull(float[] value) { + return value.clone(); } - - @Override - public Serializable disassemble(float[] value, SharedSessionContract session) { - return deepCopy( value ); - } - - @Override - public float[] assemble(Serializable cached, SharedSessionContract session) { - return deepCopy( (float[]) cached ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ImmutableObjectArrayMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ImmutableObjectArrayMutabilityPlan.java new file mode 100644 index 000000000000..dfde02f1dd36 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ImmutableObjectArrayMutabilityPlan.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.type.descriptor.java; + +/** + * A mutability plan for mutable arrays of immutable, non-primitive objects. + *

+ * Since the elements themselves are immutable, the deep copy can be implemented with a shallow copy. + * + * @author Steve Ebersole + */ +public final class ImmutableObjectArrayMutabilityPlan extends MutableMutabilityPlan { + @SuppressWarnings("rawtypes") + private static final ImmutableObjectArrayMutabilityPlan INSTANCE = new ImmutableObjectArrayMutabilityPlan(); + + @SuppressWarnings("unchecked") // Works for any T + public static ImmutableObjectArrayMutabilityPlan get() { + return (ImmutableObjectArrayMutabilityPlan) INSTANCE; + } + + public T[] deepCopyNotNull(T[] value) { + return value.clone(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java index 3dc831ee2b05..561db537ce57 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java @@ -4,7 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.io.Serializable; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.ArrayList; @@ -13,7 +12,6 @@ import java.util.List; import org.hibernate.HibernateException; -import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; import org.hibernate.internal.build.AllowReflection; @@ -185,27 +183,10 @@ else if ( value instanceof Collection collection ) { throw unknownWrap( value.getClass() ); } - private static class ArrayMutabilityPlan implements MutabilityPlan { - - @Override - public boolean isMutable() { - return true; - } - + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { @Override - public int[] deepCopy(int[] value) { - return value == null ? null : value.clone(); + protected int[] deepCopyNotNull(int[] value) { + return value.clone(); } - - @Override - public Serializable disassemble(int[] value, SharedSessionContract session) { - return deepCopy( value ); - } - - @Override - public int[] assemble(Serializable cached, SharedSessionContract session) { - return deepCopy( (int[]) cached ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java index 5369978dca61..d70f37375e9a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java @@ -4,7 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.io.Serializable; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.ArrayList; @@ -13,7 +12,6 @@ import java.util.List; import org.hibernate.HibernateException; -import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; import org.hibernate.internal.build.AllowReflection; @@ -185,27 +183,10 @@ else if ( value instanceof Collection collection ) { throw unknownWrap( value.getClass() ); } - private static class ArrayMutabilityPlan implements MutabilityPlan { - - @Override - public boolean isMutable() { - return true; - } - + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { @Override - public long[] deepCopy(long[] value) { - return value == null ? null : value.clone(); + protected long[] deepCopyNotNull(long[] value) { + return value.clone(); } - - @Override - public Serializable disassemble(long[] value, SharedSessionContract session) { - return deepCopy( value ); - } - - @Override - public long[] assemble(Serializable cached, SharedSessionContract session) { - return deepCopy( (long[]) cached ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java index c323ae1b1573..d7ce8279b1ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java @@ -28,9 +28,8 @@ public class PrimitiveByteArrayJavaType extends AbstractClassJavaType implements VersionJavaType { public static final PrimitiveByteArrayJavaType INSTANCE = new PrimitiveByteArrayJavaType(); - @SuppressWarnings("unchecked") public PrimitiveByteArrayJavaType() { - super( byte[].class, ArrayMutabilityPlan.INSTANCE, RowVersionComparator.INSTANCE ); + super( byte[].class, new ArrayMutabilityPlan(), RowVersionComparator.INSTANCE ); } @Override @@ -160,4 +159,11 @@ public byte[] next( Integer scale, SharedSessionContractImplementor session) { return current; } + + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { + @Override + protected byte[] deepCopyNotNull(byte[] value) { + return value.clone(); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java index f44d1aab3033..ae79529b8f1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveCharacterArrayJavaType.java @@ -24,7 +24,7 @@ public class PrimitiveCharacterArrayJavaType extends AbstractClassJavaType char[] coerce(X value, CoercionContext coercionContext) { return wrap( value, null ); } + + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { + @Override + protected char[] deepCopyNotNull(char[] value) { + return value.clone(); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java index 981cfd87df0f..f4615d39d8d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java @@ -4,7 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.io.Serializable; import java.lang.reflect.Array; import java.sql.SQLException; import java.util.ArrayList; @@ -13,7 +12,6 @@ import java.util.List; import org.hibernate.HibernateException; -import org.hibernate.SharedSessionContract; import org.hibernate.engine.jdbc.BinaryStream; import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream; import org.hibernate.internal.build.AllowReflection; @@ -185,27 +183,10 @@ else if ( value instanceof Collection collection ) { throw unknownWrap( value.getClass() ); } - private static class ArrayMutabilityPlan implements MutabilityPlan { - - @Override - public boolean isMutable() { - return true; - } - + private static class ArrayMutabilityPlan extends MutableMutabilityPlan { @Override - public short[] deepCopy(short[] value) { - return value == null ? null : value.clone(); + protected short[] deepCopyNotNull(short[] value) { + return value.clone(); } - - @Override - public Serializable disassemble(short[] value, SharedSessionContract session) { - return deepCopy( value ); - } - - @Override - public short[] assemble(Serializable cached, SharedSessionContract session) { - return deepCopy( (short[]) cached ); - } - } } From 06cbf3b6df167934904d6033fde86fd5a26c7d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 3 Jan 2025 15:18:04 +0100 Subject: [PATCH 4/6] HHH-18976 Simplify MultiKeyLoadHelper#resolveArrayJdbcMapping No functional impact, it's just less redundant. --- .../loader/ast/internal/CollectionBatchLoaderArrayParam.java | 5 ----- .../loader/ast/internal/EntityBatchLoaderArrayParam.java | 1 - .../loader/ast/internal/MultiIdEntityLoaderArrayParam.java | 1 - .../hibernate/loader/ast/internal/MultiKeyLoadHelper.java | 3 ++- .../loader/ast/internal/MultiNaturalIdLoaderArrayParam.java | 5 ----- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java index 029dcd38671e..24878a20b6be 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java @@ -32,7 +32,6 @@ import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; -import org.hibernate.type.BasicType; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.hasSingleId; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.trimIdBatch; @@ -74,11 +73,7 @@ public CollectionBatchLoaderArrayParam( .getClass(); keyDomainType = getKeyType( keyDescriptor.getKeyPart() ); - final BasicType arrayBasicType = getSessionFactory().getTypeConfiguration() - .getBasicTypeRegistry() - .getRegisteredType( jdbcArrayClass ); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - arrayBasicType, jdbcMapping, jdbcArrayClass, getSessionFactory() diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java index 3a5213eb19fb..4289c88bb1d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java @@ -80,7 +80,6 @@ public EntityBatchLoaderArrayParam( final Class arrayClass = Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 ).getClass(); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - sessionFactory.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( arrayClass ), identifierMapping.getJdbcMapping(), arrayClass, sessionFactory diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java index 66724a8ab7f2..d6bb4dbada17 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java @@ -51,7 +51,6 @@ public MultiIdEntityLoaderArrayParam( super( entityDescriptor, sessionFactory ); final Class idArrayClass = idArray.getClass(); arrayJdbcMapping = resolveArrayJdbcMapping( - getSessionFactory().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( idArrayClass ), getIdentifierMapping().getJdbcMapping(), idArrayClass, getSessionFactory() diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java index 87e4bd6b451b..680158497dec 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java @@ -27,10 +27,11 @@ public static boolean supportsSqlArrayType(Dialect dialect) { } public static JdbcMapping resolveArrayJdbcMapping( - BasicType arrayBasicType, JdbcMapping keyMapping, Class arrayClass, SessionFactoryImplementor sessionFactory) { + BasicType arrayBasicType = sessionFactory.getTypeConfiguration().getBasicTypeRegistry() + .getRegisteredType( arrayClass ); if ( arrayBasicType != null ) { return arrayBasicType; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java index a4a0d92dc0ff..46ae21542f2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java @@ -23,8 +23,6 @@ import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.type.BasicType; -import org.hibernate.type.BasicTypeRegistry; /** * Standard MultiNaturalIdLoader implementation @@ -77,10 +75,7 @@ public List multiLoad(K[] naturalIds, MultiNaturalIdLoadOptions loadOptio ? LockOptions.NONE : loadOptions.getLockOptions(); - final BasicTypeRegistry basicTypeRegistry = sessionFactory.getTypeConfiguration().getBasicTypeRegistry(); - final BasicType arrayBasicType = basicTypeRegistry.getRegisteredType( keyArrayClass ); final JdbcMapping arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - arrayBasicType, getNaturalIdMapping().getSingleJdbcMapping(), keyArrayClass, sessionFactory From d7551cba8a23032fc1da321be0cd8cd96666bd5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 3 Jan 2025 14:52:15 +0100 Subject: [PATCH 5/6] HHH-18976 Get rid of dynamic array instantiations used only to resolve type descriptors --- .../CollectionBatchLoaderArrayParam.java | 6 +- .../internal/EntityBatchLoaderArrayParam.java | 6 +- .../MultiIdEntityLoaderArrayParam.java | 4 +- .../ast/internal/MultiKeyLoadHelper.java | 10 +-- .../MultiNaturalIdLoaderArrayParam.java | 7 +- .../org/hibernate/type/BasicTypeRegistry.java | 4 + .../descriptor/java/spi/JavaTypeRegistry.java | 88 +++++++++++-------- 7 files changed, 67 insertions(+), 58 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java index 24878a20b6be..e0882da3efd8 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionBatchLoaderArrayParam.java @@ -51,7 +51,6 @@ public class CollectionBatchLoaderArrayParam private final SelectStatement sqlSelect; private final JdbcOperationQuerySelect jdbcSelectOperation; - @AllowReflection public CollectionBatchLoaderArrayParam( int domainBatchSize, LoadQueryInfluencers loadQueryInfluencers, @@ -69,13 +68,12 @@ public CollectionBatchLoaderArrayParam( final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor(); final JdbcMapping jdbcMapping = keyDescriptor.getSingleJdbcMapping(); - final Class jdbcArrayClass = Array.newInstance( jdbcMapping.getJdbcJavaType().getJavaTypeClass(), 0 ) - .getClass(); + final Class jdbcJavaTypeClass = jdbcMapping.getJdbcJavaType().getJavaTypeClass(); keyDomainType = getKeyType( keyDescriptor.getKeyPart() ); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( jdbcMapping, - jdbcArrayClass, + jdbcJavaTypeClass, getSessionFactory() ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java index 4289c88bb1d0..40dfc5839736 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityBatchLoaderArrayParam.java @@ -59,7 +59,6 @@ public class EntityBatchLoaderArrayParam * {@link EntityIdentifierMapping} is not available at that time. On first use, we know we * have it available */ - @AllowReflection public EntityBatchLoaderArrayParam( int domainBatchSize, EntityMappingType entityDescriptor, @@ -77,11 +76,10 @@ public EntityBatchLoaderArrayParam( } identifierMapping = (BasicEntityIdentifierMapping) getLoadable().getIdentifierMapping(); - final Class arrayClass = - Array.newInstance( identifierMapping.getJavaType().getJavaTypeClass(), 0 ).getClass(); + final Class idClass = identifierMapping.getJavaType().getJavaTypeClass(); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( identifierMapping.getJdbcMapping(), - arrayClass, + idClass, sessionFactory ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java index d6bb4dbada17..86a40d030da6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java @@ -49,10 +49,10 @@ public MultiIdEntityLoaderArrayParam( EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { super( entityDescriptor, sessionFactory ); - final Class idArrayClass = idArray.getClass(); + final Class idClass = idArray.getClass().getComponentType(); arrayJdbcMapping = resolveArrayJdbcMapping( getIdentifierMapping().getJdbcMapping(), - idArrayClass, + idClass, getSessionFactory() ); jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java index 680158497dec..424578ba8083 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadHelper.java @@ -28,10 +28,10 @@ public static boolean supportsSqlArrayType(Dialect dialect) { public static JdbcMapping resolveArrayJdbcMapping( JdbcMapping keyMapping, - Class arrayClass, + Class elementClass, SessionFactoryImplementor sessionFactory) { BasicType arrayBasicType = sessionFactory.getTypeConfiguration().getBasicTypeRegistry() - .getRegisteredType( arrayClass ); + .getRegisteredArrayType( elementClass ); if ( arrayBasicType != null ) { return arrayBasicType; } @@ -39,9 +39,9 @@ public static JdbcMapping resolveArrayJdbcMapping( final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration(); final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); - final JavaType rawArrayJavaType = javaTypeRegistry.resolveDescriptor( arrayClass ); - if ( !(rawArrayJavaType instanceof BasicPluralJavaType arrayJavaType) ) { - throw new IllegalArgumentException( "Expecting BasicPluralJavaType for array class `" + arrayClass.getName() + "`, but got `" + rawArrayJavaType + "`" ); + final JavaType rawArrayJavaType = javaTypeRegistry.resolveArrayDescriptor( elementClass ); + if ( !(rawArrayJavaType instanceof BasicPluralJavaType arrayJavaType ) ) { + throw new IllegalArgumentException( "Expecting BasicPluralJavaType for array class `" + elementClass.getTypeName() + "[]`, but got `" + rawArrayJavaType + "`" ); } //noinspection unchecked,rawtypes diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java index 46ae21542f2c..dad4597ecdb6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoaderArrayParam.java @@ -29,15 +29,14 @@ */ public class MultiNaturalIdLoaderArrayParam implements MultiNaturalIdLoader, SqlArrayMultiKeyLoader { private final EntityMappingType entityDescriptor; - private final Class keyArrayClass; + private final Class keyClass; public MultiNaturalIdLoaderArrayParam(EntityMappingType entityDescriptor) { assert entityDescriptor.getNaturalIdMapping() instanceof SimpleNaturalIdMapping; this.entityDescriptor = entityDescriptor; - final Class keyClass = entityDescriptor.getNaturalIdMapping().getJavaType().getJavaTypeClass(); - this.keyArrayClass = LoaderHelper.createTypedArray( keyClass, 0 ).getClass(); + this.keyClass = entityDescriptor.getNaturalIdMapping().getJavaType().getJavaTypeClass(); } @Override @@ -77,7 +76,7 @@ public List multiLoad(K[] naturalIds, MultiNaturalIdLoadOptions loadOptio final JdbcMapping arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( getNaturalIdMapping().getSingleJdbcMapping(), - keyArrayClass, + keyClass, sessionFactory ); final JdbcParameter jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java index c6f7c197bb09..97354c3f813f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java @@ -117,6 +117,10 @@ public BasicType getRegisteredType(Class javaType) { return getRegisteredType( javaType.getTypeName() ); } + public BasicType getRegisteredArrayType(java.lang.reflect.Type javaElementType) { + return getRegisteredType( javaElementType.getTypeName() + "[]" ); + } + public BasicType resolve(BasicTypeReference basicTypeReference) { return getRegisteredType( basicTypeReference.getName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java index c54b21539503..641cd428e0bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java @@ -34,7 +34,7 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial private static final Logger log = Logger.getLogger( JavaTypeRegistry.class ); private final TypeConfiguration typeConfiguration; - private final ConcurrentHashMap> descriptorsByType = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> descriptorsByTypeName = new ConcurrentHashMap<>(); public JavaTypeRegistry(TypeConfiguration typeConfiguration) { this.typeConfiguration = typeConfiguration; @@ -56,7 +56,7 @@ public void addBaselineDescriptor(JavaType descriptor) { @Override public void addBaselineDescriptor(Type describedJavaType, JavaType descriptor) { performInjections( descriptor ); - descriptorsByType.put( describedJavaType, descriptor ); + descriptorsByTypeName.put( describedJavaType.getTypeName(), descriptor ); } private void performInjections(JavaType descriptor) { @@ -71,7 +71,7 @@ private void performInjections(JavaType descriptor) { // descriptor access public void forEachDescriptor(Consumer> consumer) { - descriptorsByType.values().forEach( consumer ); + descriptorsByTypeName.values().forEach( consumer ); } public JavaType getDescriptor(Type javaType) { @@ -79,7 +79,7 @@ public JavaType getDescriptor(Type javaType) { } public void addDescriptor(JavaType descriptor) { - JavaType old = descriptorsByType.put( descriptor.getJavaType(), descriptor ); + JavaType old = descriptorsByTypeName.put( descriptor.getJavaType().getTypeName(), descriptor ); if ( old != null ) { log.debugf( "JavaTypeRegistry entry replaced : %s -> %s (was %s)", @@ -93,40 +93,51 @@ public void addDescriptor(JavaType descriptor) { public JavaType findDescriptor(Type javaType) { //noinspection unchecked - return (JavaType) descriptorsByType.get( javaType ); + return (JavaType) descriptorsByTypeName.get( javaType.getTypeName() ); } public JavaType resolveDescriptor(Type javaType, Supplier> creator) { - final JavaType cached = descriptorsByType.get( javaType ); + return resolveDescriptor( javaType.getTypeName(), creator ); + } + + private JavaType resolveDescriptor(String javaTypeName, Supplier> creator) { + final JavaType cached = descriptorsByTypeName.get( javaTypeName ); if ( cached != null ) { //noinspection unchecked return (JavaType) cached; } final JavaType created = creator.get(); - descriptorsByType.put( javaType, created ); + descriptorsByTypeName.put( javaTypeName, created ); return created; } public JavaType resolveDescriptor(Type javaType) { - return resolveDescriptor( javaType, (elementJavaType, typeConfiguration) -> { - final MutabilityPlan determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( - elementJavaType, - typeConfiguration - ); - if ( determinedPlan != null ) { - return determinedPlan; - } + return resolveDescriptor( javaType, JavaTypeRegistry::createMutabilityPlan ); + } + + private static MutabilityPlan createMutabilityPlan(Type elementJavaType, TypeConfiguration typeConfiguration) { + final MutabilityPlan determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( + elementJavaType, + typeConfiguration + ); + if ( determinedPlan != null ) { + return determinedPlan; + } + + return MutableMutabilityPlan.INSTANCE; + } - return MutableMutabilityPlan.INSTANCE; - } ); + public JavaType resolveArrayDescriptor(Class elementJavaType) { + return resolveDescriptor( elementJavaType.getTypeName() + "[]", + () -> createArrayTypeDescriptor( elementJavaType, JavaTypeRegistry::createMutabilityPlan) ); } public JavaType resolveDescriptor( Type javaType, BiFunction> mutabilityPlanCreator) { return resolveDescriptor( - javaType, + javaType.getTypeName(), () -> { if ( javaType instanceof ParameterizedType parameterizedType ) { final JavaType rawType = findDescriptor( parameterizedType.getRawType() ); @@ -134,33 +145,32 @@ public JavaType resolveDescriptor( return rawType.createJavaType( parameterizedType, typeConfiguration ); } } - final Type elementJavaType; - JavaType elementTypeDescriptor; - if ( javaType instanceof Class && ( (Class) javaType ).isArray() ) { - elementJavaType = ( (Class) javaType ).getComponentType(); - elementTypeDescriptor = findDescriptor( elementJavaType ); - } - else { - elementJavaType = javaType; - elementTypeDescriptor = null; - } - if ( elementTypeDescriptor == null ) { - //noinspection unchecked - elementTypeDescriptor = RegistryHelper.INSTANCE.createTypeDescriptor( - elementJavaType, - () -> (MutabilityPlan) mutabilityPlanCreator.apply( elementJavaType, typeConfiguration ), - typeConfiguration - ); - } - if ( javaType != elementJavaType ) { + else if ( javaType instanceof Class javaClass && javaClass.isArray() ) { //noinspection unchecked - return (JavaType) new ArrayJavaType<>( elementTypeDescriptor ); + return (JavaType) createArrayTypeDescriptor( javaClass.getComponentType(), mutabilityPlanCreator ); } - return elementTypeDescriptor; + return createTypeDescriptor( javaType, mutabilityPlanCreator ); } ); } + private JavaType createArrayTypeDescriptor(Class elementJavaType, BiFunction> mutabilityPlanCreator) { + JavaType elementTypeDescriptor = findDescriptor( elementJavaType ); + if ( elementTypeDescriptor == null ) { + elementTypeDescriptor = createTypeDescriptor( elementJavaType, mutabilityPlanCreator ); + } + return new ArrayJavaType<>( elementTypeDescriptor ); + } + + private JavaType createTypeDescriptor(Type javaType, BiFunction> mutabilityPlanCreator) { + //noinspection unchecked + return RegistryHelper.INSTANCE.createTypeDescriptor( + javaType, + () -> (MutabilityPlan) mutabilityPlanCreator.apply( javaType, typeConfiguration ), + typeConfiguration + ); + } + public JavaType resolveManagedTypeDescriptor(Type javaType) { return resolveManagedTypeDescriptor( javaType, false ); } From d9d2fbcd221e6247b32f1cab5147030a1eafb62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 8 Jan 2025 09:40:41 +0100 Subject: [PATCH 6/6] 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. --- .../MultiIdentifierLoadAccessImpl.java | 3 +-- .../internal/AbstractMultiIdEntityLoader.java | 12 ++++----- .../MultiIdEntityLoaderArrayParam.java | 27 ++++++++++++++++--- .../internal/MultiIdEntityLoaderStandard.java | 6 +++++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java index 35ac407f81fc..66736d014bea 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/MultiIdentifierLoadAccessImpl.java @@ -19,7 +19,6 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; -import org.hibernate.loader.ast.internal.LoaderHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; @@ -191,7 +190,7 @@ public List multiLoad(List ids) { } else { return perform( () -> (List) entityPersister.multiLoad( - ids.toArray( LoaderHelper.createTypedArray( ids.get( 0 ).getClass(), ids.size() ) ), + ids.toArray( new Object[0] ), session, this ) ); 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 1e5e8951e1d2..e10290a29206 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 @@ -11,7 +11,6 @@ import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.EventSource; -import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.ast.spi.MultiIdEntityLoader; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.internal.CacheLoadHelper.PersistenceContextEntry; @@ -22,7 +21,6 @@ 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; @@ -40,15 +38,12 @@ public abstract class AbstractMultiIdEntityLoader implements MultiIdEntityLoader { private final EntityMappingType entityDescriptor; private final SessionFactoryImplementor sessionFactory; - private final EntityIdentifierMapping identifierMapping; - protected final Object[] idArray; + protected final EntityIdentifierMapping identifierMapping; - @AllowReflection 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() { @@ -303,10 +298,13 @@ else if ( unresolvedIds.size() == ids.length ) { } else { // we need to load only some the ids - return unresolvedIds.toArray( idArray ); + return toIdArray( unresolvedIds ); } } + // Depending on the implementation, a specific subtype of Object[] (e.g. Integer[]) may be needed. + protected abstract Object[] toIdArray(List ids); + private boolean isIdCoercionEnabled() { return !getSessionFactory().getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java index 86a40d030da6..26b16931bec4 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java @@ -4,6 +4,8 @@ */ package org.hibernate.loader.ast.internal; +import java.lang.reflect.Array; +import java.util.Arrays; import java.util.List; import org.hibernate.LockOptions; @@ -12,6 +14,7 @@ import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.spi.EventSource; +import org.hibernate.internal.build.AllowReflection; import org.hibernate.loader.ast.spi.MultiIdLoadOptions; import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; @@ -44,12 +47,15 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoader implements SqlArrayMultiKeyLoader { private final JdbcMapping arrayJdbcMapping; private final JdbcParameter jdbcParameter; + protected final Object[] idArray; + @AllowReflection public MultiIdEntityLoaderArrayParam( EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { super( entityDescriptor, sessionFactory ); - final Class idClass = idArray.getClass().getComponentType(); + final Class idClass = identifierMapping.getJavaType().getJavaTypeClass(); + idArray = (Object[]) Array.newInstance( idClass, 0 ); arrayJdbcMapping = resolveArrayJdbcMapping( getIdentifierMapping().getJdbcMapping(), idClass, @@ -117,7 +123,7 @@ protected void loadEntitiesById( final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(1); jdbcParameterBindings.addBinding( jdbcParameter, - new JdbcParameterBindingImpl( arrayJdbcMapping, idsInBatch.toArray( idArray ) ) ); + new JdbcParameterBindingImpl( arrayJdbcMapping, toIdArray( idsInBatch ) ) ); getJdbcSelectExecutor().executeQuery( getSqlAstTranslatorFactory().buildSelectTranslator( getSessionFactory(), sqlAst ) @@ -165,7 +171,7 @@ protected void loadEntitiesWithUnresolvedIds( .translate( NO_BINDINGS, QueryOptions.NONE ); final List databaseResults = loadByArrayParameter( - unresolvableIds, + toIdArray( unresolvableIds ), sqlAst, jdbcSelectOperation, jdbcParameter, @@ -189,4 +195,19 @@ protected void loadEntitiesWithUnresolvedIds( } } + @Override + protected Object[] toIdArray(List ids) { + return ids.toArray( idArray ); + } + + protected Object[] toIdArray(Object[] ids) { + if ( ids.getClass().equals( idArray.getClass() ) ) { + return ids; + } + else { + Object[] typedIdArray = Arrays.copyOf( idArray, ids.length ); + System.arraycopy( ids, 0, typedIdArray, 0, ids.length ); + return typedIdArray; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java index cd17b224483a..b5a9a2ef33d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java @@ -218,4 +218,10 @@ protected void loadEntitiesWithUnresolvedIds( idPosition += batchSize; } } + + @Override + protected Object[] toIdArray(List ids) { + // This loader implementation doesn't need arrays to have a specific type, Object[] will do. + return ids.toArray( new Object[0] ); + } }