HHH-16481 - Add a new WrapperArrayHandling for enabled JPA compliance
This commit is contained in:
parent
d7c5db47f6
commit
959858f8bc
|
@ -61,6 +61,7 @@ import org.hibernate.cache.spi.RegionFactory;
|
||||||
import org.hibernate.cache.spi.access.AccessType;
|
import org.hibernate.cache.spi.access.AccessType;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.MetadataSourceType;
|
import org.hibernate.cfg.MetadataSourceType;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.TimeZoneSupport;
|
import org.hibernate.dialect.TimeZoneSupport;
|
||||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||||
import org.hibernate.engine.config.spi.StandardConverters;
|
import org.hibernate.engine.config.spi.StandardConverters;
|
||||||
|
@ -70,14 +71,17 @@ import org.hibernate.id.factory.IdentifierGeneratorFactory;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.log.DeprecationLogger;
|
import org.hibernate.internal.log.DeprecationLogger;
|
||||||
|
import org.hibernate.internal.util.NullnessHelper;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
|
import org.hibernate.jpa.spi.JpaCompliance;
|
||||||
import org.hibernate.metamodel.CollectionClassification;
|
import org.hibernate.metamodel.CollectionClassification;
|
||||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
import org.hibernate.service.spi.ServiceException;
|
import org.hibernate.service.spi.ServiceException;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.WrapperArrayHandling;
|
import org.hibernate.type.WrapperArrayHandling;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
import org.hibernate.usertype.UserType;
|
import org.hibernate.usertype.UserType;
|
||||||
|
@ -88,7 +92,9 @@ import jakarta.persistence.AttributeConverter;
|
||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
import jakarta.persistence.SharedCacheMode;
|
import jakarta.persistence.SharedCacheMode;
|
||||||
|
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.JPA_COMPLIANCE;
|
||||||
import static org.hibernate.cfg.AvailableSettings.WRAPPER_ARRAY_HANDLING;
|
import static org.hibernate.cfg.AvailableSettings.WRAPPER_ARRAY_HANDLING;
|
||||||
|
import static org.hibernate.engine.config.spi.StandardConverters.BOOLEAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -475,7 +481,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
||||||
|
|
||||||
this.implicitlyQuoteIdentifiers = configService.getSetting(
|
this.implicitlyQuoteIdentifiers = configService.getSetting(
|
||||||
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
|
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -623,30 +629,30 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
||||||
this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry );
|
this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry );
|
||||||
|
|
||||||
this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( configService );
|
this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( configService );
|
||||||
this.wrapperArrayHandling = resolveWrapperArrayHandling( configService );
|
this.wrapperArrayHandling = resolveWrapperArrayHandling( configService, serviceRegistry );
|
||||||
this.multiTenancyEnabled = JdbcEnvironmentImpl.isMultiTenancyEnabled( serviceRegistry );
|
this.multiTenancyEnabled = JdbcEnvironmentImpl.isMultiTenancyEnabled( serviceRegistry );
|
||||||
|
|
||||||
this.xmlMappingEnabled = configService.getSetting(
|
this.xmlMappingEnabled = configService.getSetting(
|
||||||
AvailableSettings.XML_MAPPING_ENABLED,
|
AvailableSettings.XML_MAPPING_ENABLED,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
this.implicitDiscriminatorsForJoinedInheritanceSupported = configService.getSetting(
|
this.implicitDiscriminatorsForJoinedInheritanceSupported = configService.getSetting(
|
||||||
AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
|
AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
this.explicitDiscriminatorsForJoinedInheritanceSupported = !configService.getSetting(
|
this.explicitDiscriminatorsForJoinedInheritanceSupported = !configService.getSetting(
|
||||||
AvailableSettings.IGNORE_EXPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
|
AvailableSettings.IGNORE_EXPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
this.implicitlyForceDiscriminatorInSelect = configService.getSetting(
|
this.implicitlyForceDiscriminatorInSelect = configService.getSetting(
|
||||||
AvailableSettings.FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT,
|
AvailableSettings.FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -710,7 +716,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
||||||
|
|
||||||
this.specjProprietarySyntaxEnabled = configService.getSetting(
|
this.specjProprietarySyntaxEnabled = configService.getSetting(
|
||||||
"hibernate.enable_specj_proprietary_syntax",
|
"hibernate.enable_specj_proprietary_syntax",
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -760,7 +766,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
||||||
|
|
||||||
this.useNationalizedCharacterData = configService.getSetting(
|
this.useNationalizedCharacterData = configService.getSetting(
|
||||||
AvailableSettings.USE_NATIONALIZED_CHARACTER_DATA,
|
AvailableSettings.USE_NATIONALIZED_CHARACTER_DATA,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -772,7 +778,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
||||||
|
|
||||||
allowExtensionsInCdi = configService.getSetting(
|
allowExtensionsInCdi = configService.getSetting(
|
||||||
AvailableSettings.ALLOW_EXTENSIONS_IN_CDI,
|
AvailableSettings.ALLOW_EXTENSIONS_IN_CDI,
|
||||||
StandardConverters.BOOLEAN,
|
BOOLEAN,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1011,19 +1017,38 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WrapperArrayHandling resolveWrapperArrayHandling(
|
private static WrapperArrayHandling resolveWrapperArrayHandling(
|
||||||
ConfigurationService configService) {
|
ConfigurationService configService,
|
||||||
return configService.getSetting(
|
StandardServiceRegistry serviceRegistry) {
|
||||||
WRAPPER_ARRAY_HANDLING,
|
final WrapperArrayHandling setting = NullnessHelper.coalesceSuppliedValues(
|
||||||
value -> {
|
() -> configService.getSetting(
|
||||||
if ( value == null ) {
|
WRAPPER_ARRAY_HANDLING,
|
||||||
throw new IllegalArgumentException( "Null value passed to convert" );
|
WrapperArrayHandling::interpretExternalSettingLeniently
|
||||||
}
|
),
|
||||||
|
() -> resolveFallbackWrapperArrayHandling( configService, serviceRegistry )
|
||||||
return value instanceof WrapperArrayHandling
|
|
||||||
? (WrapperArrayHandling) value
|
|
||||||
: WrapperArrayHandling.valueOf( value.toString().toUpperCase( Locale.ROOT ) );
|
|
||||||
},
|
|
||||||
WrapperArrayHandling.DISALLOW
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( setting == WrapperArrayHandling.PICK ) {
|
||||||
|
final Dialect dialect = serviceRegistry.getService( JdbcServices.class ).getDialect();
|
||||||
|
if ( dialect.supportsStandardArrays()
|
||||||
|
&& ( dialect.getPreferredSqlTypeCodeForArray() == SqlTypes.ARRAY
|
||||||
|
|| dialect.getPreferredSqlTypeCodeForArray() == SqlTypes.SQLXML ) ) {
|
||||||
|
return WrapperArrayHandling.ALLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WrapperArrayHandling.LEGACY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
};
|
||||||
|
|
||||||
|
private static WrapperArrayHandling resolveFallbackWrapperArrayHandling(
|
||||||
|
ConfigurationService configService,
|
||||||
|
StandardServiceRegistry serviceRegistry) {
|
||||||
|
if ( configService.getSetting( JPA_COMPLIANCE, BOOLEAN ) == Boolean.TRUE ) {
|
||||||
|
// JPA compliance was enabled. Use PICK
|
||||||
|
return WrapperArrayHandling.PICK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WrapperArrayHandling.DISALLOW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.type;
|
package org.hibernate.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
import org.hibernate.type.descriptor.java.CharacterArrayJavaType;
|
||||||
|
|
||||||
|
@ -21,7 +24,8 @@ public enum WrapperArrayHandling {
|
||||||
/**
|
/**
|
||||||
* Throw an informative and actionable error if the types are used explicitly in the domain model
|
* Throw an informative and actionable error if the types are used explicitly in the domain model
|
||||||
*
|
*
|
||||||
* @implNote The default behavior
|
* @implNote The default behavior; unless {@linkplain AvailableSettings#JPA_COMPLIANCE JPA compliance}
|
||||||
|
* is enabled - see {@linkplain #PICK}
|
||||||
*/
|
*/
|
||||||
DISALLOW,
|
DISALLOW,
|
||||||
|
|
||||||
|
@ -42,5 +46,37 @@ public enum WrapperArrayHandling {
|
||||||
* @apiNote Hibernate recommends users who want the legacy semantic change the domain model to use
|
* @apiNote Hibernate recommends users who want the legacy semantic change the domain model to use
|
||||||
* {@code byte[]} and {@code char[]} rather than using this setting.
|
* {@code byte[]} and {@code char[]} rather than using this setting.
|
||||||
*/
|
*/
|
||||||
LEGACY
|
LEGACY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hibernate will pick between {@linkplain #ALLOW} and {@linkplain #LEGACY} depending on
|
||||||
|
* whether the Dialect supports SQL {@code ARRAY} types.
|
||||||
|
*
|
||||||
|
* @implNote The default if {@linkplain AvailableSettings#JPA_COMPLIANCE JPA compliance} is enabled.
|
||||||
|
*/
|
||||||
|
PICK;
|
||||||
|
|
||||||
|
public static WrapperArrayHandling interpretExternalSetting(Object value) {
|
||||||
|
if ( value == null ) {
|
||||||
|
throw new IllegalArgumentException( "Null value passed to convert" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return value instanceof WrapperArrayHandling
|
||||||
|
? (WrapperArrayHandling) value
|
||||||
|
: valueOf( value.toString().toUpperCase( Locale.ROOT ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form of {@link #interpretExternalSetting(Object)} which allows incoming {@code null} values and
|
||||||
|
* simply returns {@code null}. Useful for chained resolutions
|
||||||
|
*/
|
||||||
|
public static WrapperArrayHandling interpretExternalSettingLeniently(Object value) {
|
||||||
|
if ( value == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value instanceof WrapperArrayHandling
|
||||||
|
? (WrapperArrayHandling) value
|
||||||
|
: valueOf( value.toString().toUpperCase( Locale.ROOT ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.jpa.mapping;
|
||||||
|
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.boot.Metadata;
|
||||||
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
|
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.Setting;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Basic;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class WrapperArrayHandlingTests {
|
||||||
|
@Test
|
||||||
|
@ServiceRegistry(
|
||||||
|
settings = @Setting( name = AvailableSettings.JPA_COMPLIANCE, value = "true" )
|
||||||
|
)
|
||||||
|
void testComplianceEnabled(ServiceRegistryScope scope) {
|
||||||
|
try ( SessionFactory sessionFactory = buildSessionFactory( scope ) ) {
|
||||||
|
// we expect this one to pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SessionFactory buildSessionFactory(ServiceRegistryScope scope) {
|
||||||
|
final MetadataSources metadataSources = new MetadataSources( scope.getRegistry() );
|
||||||
|
final Metadata metadata = metadataSources.addAnnotatedClasses( TheEntity.class ).buildMetadata();
|
||||||
|
return metadata.buildSessionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ServiceRegistry(
|
||||||
|
settings = @Setting( name = AvailableSettings.JPA_COMPLIANCE, value = "false" )
|
||||||
|
)
|
||||||
|
void testComplianceDisabled(ServiceRegistryScope scope) {
|
||||||
|
try ( SessionFactory sessionFactory = buildSessionFactory( scope ) ) {
|
||||||
|
// however, this one should fall because DISALLOW is the default
|
||||||
|
fail( "Expecting an exception due to DISALLOW" );
|
||||||
|
}
|
||||||
|
catch (Exception expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "TheEntity" )
|
||||||
|
@Table( name = "TheEntity" )
|
||||||
|
public static class TheEntity {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
@Basic
|
||||||
|
private String name;
|
||||||
|
@Basic
|
||||||
|
private Character[] characters;
|
||||||
|
@Basic
|
||||||
|
private Byte[] bytes;
|
||||||
|
|
||||||
|
protected TheEntity() {
|
||||||
|
// for use by Hibernate
|
||||||
|
}
|
||||||
|
|
||||||
|
public TheEntity(Integer id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Character[] getCharacters() {
|
||||||
|
return characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharacters(Character[] characters) {
|
||||||
|
this.characters = characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(Byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue