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..525845699382 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 @@ -12,14 +12,9 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.CollectionBatchLoader; -import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.ValuedModelPart; -import org.hibernate.metamodel.mapping.internal.IdClassEmbeddable; import org.hibernate.sql.results.internal.ResultsHelper; -import java.lang.reflect.Array; - import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.hasSingleId; import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.trimIdBatch; import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER; @@ -128,10 +123,7 @@ protected void finishInitializingKey(Object key, SharedSessionContractImplemento Object[] resolveKeysToInitialize(Object keyBeingLoaded, SharedSessionContractImplementor session) { final int length = getDomainBatchSize(); - final Object[] keysToInitialize = (Object[]) Array.newInstance( - getKeyType( getLoadable().getKeyDescriptor().getKeyPart() ), - length - ); + final Object[] keysToInitialize = new Object[length]; session.getPersistenceContextInternal().getBatchFetchQueue() .collectBatchLoadableCollectionKeys( length, @@ -143,15 +135,5 @@ Object[] resolveKeysToInitialize(Object keyBeingLoaded, SharedSessionContractImp return trimIdBatch( length, keysToInitialize ); } - protected Class getKeyType(ValuedModelPart keyPart) { - if ( keyPart instanceof NonAggregatedIdentifierMapping ) { - final IdClassEmbeddable idClassEmbeddable = ( (NonAggregatedIdentifierMapping) keyPart ).getIdClassEmbeddable(); - if ( idClassEmbeddable != null ) { - return idClassEmbeddable.getMappedJavaType().getJavaTypeClass(); - } - } - return keyPart.getJavaType().getJavaTypeClass(); - } - } 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..6ef6e3cdba21 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 @@ -21,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; @@ -39,14 +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; 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() { @@ -301,7 +298,7 @@ else if ( unresolvedIds.size() == ids.length ) { } else { // we need to load only some the ids - return unresolvedIds.toArray( idArray ); + return unresolvedIds.toArray( new Object[0] ); } } 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..7997607f4871 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 @@ -4,8 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.lang.reflect.Array; - import org.hibernate.LockOptions; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionKey; @@ -31,7 +29,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; @@ -45,7 +42,6 @@ public class CollectionBatchLoaderArrayParam extends AbstractCollectionBatchLoader implements SqlArrayMultiKeyLoader { - private final Class keyDomainType; private final JdbcMapping arrayJdbcMapping; private final JdbcParameter jdbcParameter; private final SelectStatement sqlSelect; @@ -68,17 +64,11 @@ public CollectionBatchLoaderArrayParam( final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor(); final JdbcMapping jdbcMapping = keyDescriptor.getSingleJdbcMapping(); - final Class jdbcArrayClass = Array.newInstance( jdbcMapping.getJdbcJavaType().getJavaTypeClass(), 0 ) - .getClass(); - keyDomainType = getKeyType( keyDescriptor.getKeyPart() ); + final Class jdbcJavaTypeClass = jdbcMapping.getJdbcJavaType().getJavaTypeClass(); - final BasicType arrayBasicType = getSessionFactory().getTypeConfiguration() - .getBasicTypeRegistry() - .getRegisteredType( jdbcArrayClass ); arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping( - arrayBasicType, jdbcMapping, - jdbcArrayClass, + jdbcJavaTypeClass, getSessionFactory() ); @@ -127,15 +117,8 @@ private PersistentCollection loadEmbeddable( } final int length = getDomainBatchSize(); - final Object[] keysToInitialize = (Object[]) Array.newInstance( - jdbcParameter.getExpressionType() - .getSingleJdbcMapping() - .getJdbcJavaType() - .getJavaTypeClass() - .getComponentType(), - length - ); - final Object[] embeddedKeys = (Object[]) Array.newInstance( keyDomainType, length ); + final Object[] keysToInitialize = new Object[length]; + final Object[] embeddedKeys = new Object[length]; session.getPersistenceContextInternal().getBatchFetchQueue() .collectBatchLoadableCollectionKeys( length, @@ -221,7 +204,7 @@ Object[] resolveKeysToInitialize(Object keyBeingLoaded, SharedSessionContractImp if( keyDescriptor.isEmbedded()){ assert keyDescriptor.getJdbcTypeCount() == 1; final int length = getDomainBatchSize(); - final Object[] keysToInitialize = (Object[]) Array.newInstance( keyDescriptor.getSingleJdbcMapping().getJdbcJavaType().getJavaTypeClass(), length ); + final Object[] keysToInitialize = new Object[length]; session.getPersistenceContextInternal().getBatchFetchQueue() .collectBatchLoadableCollectionKeys( length, 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..bd1cbc7f8d98 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 @@ -4,7 +4,6 @@ */ package org.hibernate.loader.ast.internal; -import java.lang.reflect.Array; import java.util.Arrays; import java.util.Locale; @@ -75,12 +74,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 ); @@ -108,8 +105,7 @@ public int getDomainBatchSize() { protected Object[] resolveIdsToInitialize(Object pkValue, SharedSessionContractImplementor session) { //TODO: should this really be different to EntityBatchLoaderInPredicate impl? - final Class idType = identifierMapping.getJavaType().getJavaTypeClass(); - final Object[] idsToLoad = (Object[]) Array.newInstance( idType, domainBatchSize ); + final Object[] idsToLoad = new Object[domainBatchSize]; session.getPersistenceContextInternal().getBatchFetchQueue() .collectBatchLoadableEntityIds( domainBatchSize, 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..931b87bfc837 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,9 @@ public MultiIdEntityLoaderArrayParam( EntityMappingType entityDescriptor, SessionFactoryImplementor sessionFactory) { super( entityDescriptor, sessionFactory ); - final Class idArrayClass = idArray.getClass(); arrayJdbcMapping = resolveArrayJdbcMapping( - getSessionFactory().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( idArrayClass ), getIdentifierMapping().getJdbcMapping(), - idArrayClass, + identifierMapping.getJavaType().getJavaTypeClass(), getSessionFactory() ); jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping ); @@ -118,7 +116,7 @@ protected void loadEntitiesById( final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(1); jdbcParameterBindings.addBinding( jdbcParameter, - new JdbcParameterBindingImpl( arrayJdbcMapping, idsInBatch.toArray( idArray ) ) ); + new JdbcParameterBindingImpl( arrayJdbcMapping, idsInBatch ) ); getJdbcSelectExecutor().executeQuery( getSqlAstTranslatorFactory().buildSelectTranslator( getSessionFactory(), sqlAst ) 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..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..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/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/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 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/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/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/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/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 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/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/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..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..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