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/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/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..778718cd0db3 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 { @@ -58,13 +59,6 @@ public static int indexOf(Object[] array, int end, Object object) { return -1; } - @SuppressWarnings("unchecked") - public static T[] filledArray(T value, Class valueJavaType, int size) { - final T[] array = (T[]) Array.newInstance( valueJavaType, size ); - Arrays.fill( array, value ); - return array; - } - public static String[] toStringArray(Object[] objects) { int length = objects.length; String[] result = new String[length]; @@ -202,6 +196,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 ); @@ -518,10 +513,4 @@ public static void forEach(T[] array, Consumer consumer) { consumer.accept( array[ i ] ); } } - - @SuppressWarnings("unchecked") - 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..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 @@ -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; @@ -31,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; @@ -68,17 +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() ); - final BasicType arrayBasicType = getSessionFactory().getTypeConfiguration() - .getBasicTypeRegistry() - .getRegisteredType( jdbcArrayClass ); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - arrayBasicType, jdbcMapping, - jdbcArrayClass, + jdbcJavaTypeClass, getSessionFactory() ); @@ -115,6 +110,7 @@ public PersistentCollection load(Object keyBeingLoaded, SharedSessionContract } + @AllowReflection private PersistentCollection loadEmbeddable( Object keyBeingLoaded, SharedSessionContractImplementor session, @@ -216,6 +212,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..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 @@ -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; @@ -75,12 +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( - sessionFactory.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( arrayClass ), identifierMapping.getJdbcMapping(), - arrayClass, + idClass, sessionFactory ); @@ -106,6 +105,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..e88fa6aec2f8 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; @@ -168,6 +169,7 @@ public static Boolean getReadOnlyFromLoadQueryInfluencers(LoadQueryInfluencers l * * @param The key type */ + @AllowReflection public static K[] normalizeKeys( K[] keys, BasicValuedModelPart keyPart, @@ -183,7 +185,8 @@ public static K[] normalizeKeys( return keys; } - final K[] typedArray = createTypedArray( keyClass, keys.length ); + //noinspection unchecked + final K[] typedArray = (K[]) Array.newInstance( keyClass, keys.length ); final boolean coerce = !sessionFactory.getJpaMetamodel().getJpaCompliance().isLoadByIdComplianceEnabled(); if ( !coerce ) { System.arraycopy( keys, 0, typedArray, 0, keys.length ); @@ -196,17 +199,6 @@ public static K[] normalizeKeys( return typedArray; } - /** - * Creates a typed array, as opposed to a generic {@code Object[]} that holds the typed values - * - * @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 - */ - public static X[] createTypedArray(Class elementClass, @SuppressWarnings("SameParameterValue") int length) { - //noinspection unchecked - return (X[]) Array.newInstance( elementClass, length ); - } - /** * Load one or more instances of a model part (an entity or collection) * based on a SQL ARRAY parameter to specify the keys (as opposed to the 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..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,11 +49,10 @@ public MultiIdEntityLoaderArrayParam( EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { super( entityDescriptor, sessionFactory ); - final Class idArrayClass = idArray.getClass(); + final Class idClass = idArray.getClass().getComponentType(); arrayJdbcMapping = resolveArrayJdbcMapping( - getSessionFactory().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( idArrayClass ), 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 87e4bd6b451b..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 @@ -27,10 +27,11 @@ public static boolean supportsSqlArrayType(Dialect dialect) { } public static JdbcMapping resolveArrayJdbcMapping( - BasicType arrayBasicType, JdbcMapping keyMapping, - Class arrayClass, + Class elementClass, SessionFactoryImplementor sessionFactory) { + BasicType arrayBasicType = sessionFactory.getTypeConfiguration().getBasicTypeRegistry() + .getRegisteredArrayType( elementClass ); if ( arrayBasicType != null ) { return arrayBasicType; } @@ -38,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 a4a0d92dc0ff..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 @@ -23,23 +23,20 @@ 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 */ 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,12 +74,9 @@ 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, + keyClass, sessionFactory ); final JdbcParameter jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping ); 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/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/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..690a050fa57d 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 @@ -4,9 +4,9 @@ */ package org.hibernate.type.descriptor.converter.internal; -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,14 +43,12 @@ public X toDomainValue(Y relationalForm) { } @Override + @AllowReflection public Y toRelationalValue(X domainForm) { if ( domainForm == null ) { return null; } - final Object[] relationalArray = (Object[]) Array.newInstance( - elementConverter.getRelationalJavaType().getJavaTypeClass(), - domainForm.size() - ); + final Object[] relationalArray = elementConverter.getRelationalJavaType().newArray( domainForm.size() ); int i = 0; for ( Object domainValue : domainForm ) { relationalArray[i++] = elementConverter.toRelationalValue( domainValue ); 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..7e61a30ba781 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 @@ -4,8 +4,6 @@ */ package org.hibernate.type.descriptor.java; -import java.lang.reflect.Array; - import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation; @@ -84,8 +82,7 @@ BasicType createTypeUsingConverter( ColumnTypeInformation columnTypeInformation, JdbcTypeIndicators stdIndicators, BasicValueConverter valueConverter) { - final Class convertedElementClass = valueConverter.getRelationalJavaType().getJavaTypeClass(); - final Class convertedArrayClass = Array.newInstance( convertedElementClass, 0 ).getClass(); + final Class convertedArrayClass = valueConverter.getRelationalJavaType().getArrayClass(); final JavaType relationalJavaType = typeConfiguration.getJavaTypeRegistry().getDescriptor( convertedArrayClass ); return new ConvertedBasicArrayType<>( elementType, 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..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 @@ -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; /** @@ -10,11 +12,17 @@ * 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(); @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/BooleanJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java index 03fae3c28c93..865d407978b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanJavaType.java @@ -156,6 +156,11 @@ public Class getPrimitiveClass() { return boolean.class; } + @Override + public Boolean[] newArray(int numberOfElements) { + return new Boolean[numberOfElements]; + } + @Override public Class getArrayClass() { return Boolean[].class; 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..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,9 +12,9 @@ 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; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +23,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class BooleanPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final BooleanPrimitiveArrayJavaType INSTANCE = new BooleanPrimitiveArrayJavaType(); @@ -183,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/ByteJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java index 87e4f609e17e..f1ed57cd71c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ByteJavaType.java @@ -96,6 +96,11 @@ public Class getPrimitiveClass() { return byte.class; } + @Override + public Byte[] newArray(int numberOfElements) { + return new Byte[numberOfElements]; + } + @Override public Class getArrayClass() { return Byte[].class; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java index 940c5201a589..694fb63fa715 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaType.java @@ -136,6 +136,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public Calendar[] newArray(int numberOfElements) { + return new Calendar[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Calendar[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { return 0; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java index e4d49d274fe4..6b9b5de445f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaType.java @@ -154,6 +154,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public Calendar[] newArray(int numberOfElements) { + return new Calendar[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Calendar[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { return dialect.getDefaultTimestampPrecision(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java index d480795b7f68..4e671e597748 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaType.java @@ -138,6 +138,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public Calendar[] newArray(int numberOfElements) { + return new Calendar[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Calendar[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { // times represent repeating events - they 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/CharacterJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java index f240db0f10b3..5d214c385f8b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CharacterJavaType.java @@ -96,6 +96,11 @@ public Class getPrimitiveClass() { return char.class; } + @Override + public Character[] newArray(int numberOfElements) { + return new Character[numberOfElements]; + } + @Override public Class getArrayClass() { return Character[].class; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java index e9e005a724b6..a7a4638770a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java @@ -174,6 +174,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public Date[] newArray(int numberOfElements) { + return new Date[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Date[].class; + } + @Override public Date next( Date current, diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java index 191ec6462a55..c4bf9c201c4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoubleJavaType.java @@ -121,6 +121,11 @@ public Class getPrimitiveClass() { return double.class; } + @Override + public Double[] newArray(int numberOfElements) { + return new Double[numberOfElements]; + } + @Override public Class getArrayClass() { return Double[].class; 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..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,9 +12,9 @@ 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; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +23,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class DoublePrimitiveArrayJavaType extends AbstractArrayJavaType { public static final DoublePrimitiveArrayJavaType INSTANCE = new DoublePrimitiveArrayJavaType(); @@ -183,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/FloatJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java index cb200bed4d24..ab8b5161c876 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatJavaType.java @@ -119,6 +119,11 @@ public Class getPrimitiveClass() { return float.class; } + @Override + public Float[] newArray(int numberOfElements) { + return new Float[numberOfElements]; + } + @Override public Class getArrayClass() { return Float[].class; 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..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,9 +12,9 @@ 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; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +23,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class FloatPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final FloatPrimitiveArrayJavaType INSTANCE = new FloatPrimitiveArrayJavaType(); @@ -183,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/InstantJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java index 4d5e80b0bb7f..c9172ac5b30e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaType.java @@ -188,6 +188,16 @@ public Instant wrap(X value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + @Override + public Instant[] newArray(int numberOfElements) { + return new Instant[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Instant[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { return dialect.getDefaultTimestampPrecision(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java index c28bc26b7a66..32c36ac4f07c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerJavaType.java @@ -112,6 +112,11 @@ public Class getPrimitiveClass() { return int.class; } + @Override + public Integer[] newArray(int numberOfElements) { + return new Integer[numberOfElements]; + } + @Override public Class getArrayClass() { return Integer[].class; 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..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,9 +12,9 @@ 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; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +23,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class IntegerPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final IntegerPrimitiveArrayJavaType INSTANCE = new IntegerPrimitiveArrayJavaType(); @@ -183,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/JavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java index 4e1095b65c02..a312ac8c3a60 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java @@ -5,6 +5,7 @@ package org.hibernate.type.descriptor.java; import java.io.Serializable; +import java.lang.reflect.Array; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Comparator; @@ -328,6 +329,28 @@ default JavaType createJavaType(ParameterizedType parameterizedType, TypeConf return this; } + /** + * Creates a typed object array, as opposed to a generic {@code Object[]} that holds the typed values. + *

+ * The array type necessarily extends {@code Object[]}: it will never be an array of primitives like {@code int[]}. + * + * @param numberOfElements The size of the array to create + */ + @SuppressWarnings("unchecked") + default T[] newArray(int numberOfElements) { + return (T[]) Array.newInstance( getJavaTypeClass(), numberOfElements ); + } + + /** + * Get the Java type (the {@link Class} object) representing an array with elements of this {@code JavaType}. + *

+ * The array type necessarily extends {@code Object[]}: it will never be an array of primitives like {@code int[]}. + */ + @SuppressWarnings("unchecked") + default Class getArrayClass() { + return (Class) newArray( 0 ).getClass(); + } + /** * Return true if the implementation is an instance of {@link TemporalJavaType} * diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index 4859d2c16b26..1cf4641d9945 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -210,6 +210,16 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + @Override + public Date[] newArray(int numberOfElements) { + return new Date[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Date[].class; + } + @Override public String toString(Date value) { if ( value instanceof java.sql.Date ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index 279c39c9af02..0c8b08408e56 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -203,6 +203,16 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + @Override + public Date[] newArray(int numberOfElements) { + return new Date[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Date[].class; + } + @Override public String toString(Date value) { if ( value instanceof java.sql.Time time ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index 0a309d920187..0bb50a98cb2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -197,6 +197,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public Date[] newArray(int numberOfElements) { + return new Date[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return Date[].class; + } + @Override public String toString(Date value) { return LITERAL_FORMATTER.format( value.toInstant() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java index 34765bfdc209..204e3ae67f7e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java @@ -170,4 +170,14 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public LocalDate[] newArray(int numberOfElements) { + return new LocalDate[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return LocalDate[].class; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java index a1e2bb1a3f32..05999edbd5d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java @@ -168,6 +168,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public LocalDateTime[] newArray(int numberOfElements) { + return new LocalDateTime[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return LocalDateTime[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { return dialect.getDefaultTimestampPrecision(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java index 58654f51b5ca..bf0c57ef4d97 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java @@ -179,6 +179,16 @@ public boolean isWider(JavaType javaType) { }; } + @Override + public LocalTime[] newArray(int numberOfElements) { + return new LocalTime[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return LocalTime[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { // times represent repeating events - they diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java index b2762632cfb7..77d74a678945 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongJavaType.java @@ -167,6 +167,11 @@ public Class getPrimitiveClass() { return long.class; } + @Override + public Long[] newArray(int numberOfElements) { + return new Long[numberOfElements]; + } + @Override public Class getArrayClass() { return Long[].class; 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..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,9 +12,9 @@ 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; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +23,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class LongPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final LongPrimitiveArrayJavaType INSTANCE = new LongPrimitiveArrayJavaType(); @@ -183,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/OffsetDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java index bccfd17dea9e..85e3aa7b0de4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java @@ -228,6 +228,16 @@ public OffsetDateTime wrap(X value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + @Override + public OffsetDateTime[] newArray(int numberOfElements) { + return new OffsetDateTime[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return OffsetDateTime[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { return dialect.getDefaultTimestampPrecision(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java index ec55e2d78104..e81baca732f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java @@ -242,6 +242,16 @@ private static ZoneOffset getCurrentSystemOffset() { return OffsetDateTime.now().getOffset(); } + @Override + public OffsetTime[] newArray(int numberOfElements) { + return new OffsetTime[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return OffsetTime[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { // times represent repeating events - they 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/ShortJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java index 60f9278a0206..953ab1747192 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortJavaType.java @@ -103,6 +103,11 @@ public Class getPrimitiveClass() { return short.class; } + @Override + public Short[] newArray(int numberOfElements) { + return new Short[numberOfElements]; + } + @Override public Class getArrayClass() { return Short[].class; 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..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,9 +12,9 @@ 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; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.type.descriptor.WrapperOptions; @@ -24,6 +23,7 @@ * * @author Christian Beikov */ +@AllowReflection // Needed for arbitrary array wrapping/unwrapping public class ShortPrimitiveArrayJavaType extends AbstractArrayJavaType { public static final ShortPrimitiveArrayJavaType INSTANCE = new ShortPrimitiveArrayJavaType(); @@ -183,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 ); - } - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java index 35bae1aeffc9..d412d92ed98c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java @@ -190,6 +190,16 @@ public ZonedDateTime wrap(X value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + @Override + public ZonedDateTime[] newArray(int numberOfElements) { + return new ZonedDateTime[numberOfElements]; + } + + @Override + public Class getArrayClass() { + return ZonedDateTime[].class; + } + @Override public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) { return dialect.getDefaultTimestampPrecision(); 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..524a80344f7c 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 { @@ -131,9 +133,7 @@ public BasicType resolveType( ); } else { - final Class arrayClass = - Array.newInstance( valueConverter.getRelationalJavaType().getJavaTypeClass(), 0 ) - .getClass(); + final Class arrayClass = valueConverter.getRelationalJavaType().getArrayClass(); final JavaType relationalJavaType = typeConfiguration.getJavaTypeRegistry().resolveDescriptor( arrayClass ); //noinspection unchecked,rawtypes diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/DynamicModelJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/DynamicModelJavaType.java index c6c087da71a9..35d867cc7eca 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/DynamicModelJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/DynamicModelJavaType.java @@ -43,4 +43,15 @@ public Class> getJavaTypeClass() { //noinspection unchecked,rawtypes return (Class) Map.class; } + + @Override + public Map[] newArray(int numberOfElements) { + return new Map[numberOfElements]; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes", "RedundantCast"}) + public Class[]> getArrayClass() { + return (Class[]>) (Class) Map[].class; + } } 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..725f876cb24d 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 + "[]", + () -> 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 ); } 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..6b65da256323 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 @@ -67,9 +67,8 @@ public JavaType getJdbcRecommendedJavaTypeMapping( scale, typeConfiguration ); - final JavaType javaType = typeConfiguration.getJavaTypeRegistry().resolveDescriptor( - Array.newInstance( elementJavaType.getJavaTypeClass(), 0 ).getClass() - ); + final JavaType javaType = typeConfiguration.getJavaTypeRegistry() + .resolveDescriptor( elementJavaType.getArrayClass() ); if ( javaType instanceof BasicPluralType ) { //noinspection unchecked return (JavaType) javaType; @@ -145,28 +144,18 @@ protected Object[] getArray(BasicBinder binder, ValueBinder elementBin //noinspection unchecked final JavaType javaType = (JavaType) binder.getJavaType(); if ( elementJdbcType instanceof AggregateJdbcType ) { - final T[] domainObjects = (T[]) javaType.unwrap( value, Object[].class, options ); + final Object[] domainObjects = javaType.unwrap( value, Object[].class, options ); final Object[] objects = new Object[domainObjects.length]; for ( int i = 0; i < domainObjects.length; i++ ) { if ( domainObjects[i] != null ) { - objects[i] = elementBinder.getBindValue( domainObjects[i], options ); + //noinspection unchecked + objects[i] = elementBinder.getBindValue( (T) domainObjects[i], options ); } } return objects; } else { - final TypeConfiguration typeConfiguration = options.getTypeConfiguration(); - final JdbcType underlyingJdbcType = - typeConfiguration.getJdbcTypeRegistry().getDescriptor( elementJdbcType.getDefaultSqlTypeCode() ); - final Class preferredJavaTypeClass = elementJdbcType.getPreferredJavaTypeClass( options ); - final Class elementJdbcJavaTypeClass = - preferredJavaTypeClass == null - ? underlyingJdbcType.getJdbcRecommendedJavaTypeMapping(null, null, typeConfiguration ) - .getJavaTypeClass() - : preferredJavaTypeClass; - final Class arrayClass = (Class) - Array.newInstance( elementJdbcJavaTypeClass, 0 ).getClass(); - return javaType.unwrap( value, arrayClass, options ); + return javaType.unwrap( value, Object[].class, options ); } } 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..99b433204d28 --- /dev/null +++ b/rules/forbidden-apis.txt @@ -0,0 +1,18 @@ +# 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#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