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.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.MetadataSourceType;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
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.CoreMessageLogger;
|
||||
import org.hibernate.internal.log.DeprecationLogger;
|
||||
import org.hibernate.internal.util.NullnessHelper;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.jpa.spi.JpaCompliance;
|
||||
import org.hibernate.metamodel.CollectionClassification;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.service.spi.ServiceException;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.WrapperArrayHandling;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
@ -88,7 +92,9 @@ import jakarta.persistence.AttributeConverter;
|
|||
import jakarta.persistence.ConstraintMode;
|
||||
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.engine.config.spi.StandardConverters.BOOLEAN;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -475,7 +481,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
|
||||
this.implicitlyQuoteIdentifiers = configService.getSetting(
|
||||
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -623,30 +629,30 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry );
|
||||
|
||||
this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( configService );
|
||||
this.wrapperArrayHandling = resolveWrapperArrayHandling( configService );
|
||||
this.wrapperArrayHandling = resolveWrapperArrayHandling( configService, serviceRegistry );
|
||||
this.multiTenancyEnabled = JdbcEnvironmentImpl.isMultiTenancyEnabled( serviceRegistry );
|
||||
|
||||
this.xmlMappingEnabled = configService.getSetting(
|
||||
AvailableSettings.XML_MAPPING_ENABLED,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
true
|
||||
);
|
||||
|
||||
this.implicitDiscriminatorsForJoinedInheritanceSupported = configService.getSetting(
|
||||
AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
|
||||
this.explicitDiscriminatorsForJoinedInheritanceSupported = !configService.getSetting(
|
||||
AvailableSettings.IGNORE_EXPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
|
||||
this.implicitlyForceDiscriminatorInSelect = configService.getSetting(
|
||||
AvailableSettings.FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -710,7 +716,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
|
||||
this.specjProprietarySyntaxEnabled = configService.getSetting(
|
||||
"hibernate.enable_specj_proprietary_syntax",
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -760,7 +766,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
|
||||
this.useNationalizedCharacterData = configService.getSetting(
|
||||
AvailableSettings.USE_NATIONALIZED_CHARACTER_DATA,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -772,7 +778,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
|
||||
allowExtensionsInCdi = configService.getSetting(
|
||||
AvailableSettings.ALLOW_EXTENSIONS_IN_CDI,
|
||||
StandardConverters.BOOLEAN,
|
||||
BOOLEAN,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
@ -1011,19 +1017,38 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
}
|
||||
|
||||
private static WrapperArrayHandling resolveWrapperArrayHandling(
|
||||
ConfigurationService configService) {
|
||||
return configService.getSetting(
|
||||
WRAPPER_ARRAY_HANDLING,
|
||||
value -> {
|
||||
if ( value == null ) {
|
||||
throw new IllegalArgumentException( "Null value passed to convert" );
|
||||
}
|
||||
|
||||
return value instanceof WrapperArrayHandling
|
||||
? (WrapperArrayHandling) value
|
||||
: WrapperArrayHandling.valueOf( value.toString().toUpperCase( Locale.ROOT ) );
|
||||
},
|
||||
WrapperArrayHandling.DISALLOW
|
||||
ConfigurationService configService,
|
||||
StandardServiceRegistry serviceRegistry) {
|
||||
final WrapperArrayHandling setting = NullnessHelper.coalesceSuppliedValues(
|
||||
() -> configService.getSetting(
|
||||
WRAPPER_ARRAY_HANDLING,
|
||||
WrapperArrayHandling::interpretExternalSettingLeniently
|
||||
),
|
||||
() -> resolveFallbackWrapperArrayHandling( configService, serviceRegistry )
|
||||
);
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
||||
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
|
||||
*
|
||||
* @implNote The default behavior
|
||||
* @implNote The default behavior; unless {@linkplain AvailableSettings#JPA_COMPLIANCE JPA compliance}
|
||||
* is enabled - see {@linkplain #PICK}
|
||||
*/
|
||||
DISALLOW,
|
||||
|
||||
|
@ -42,5 +46,37 @@ public enum WrapperArrayHandling {
|
|||
* @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.
|
||||
*/
|
||||
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