HHH-16311 - Migrate away from UserType for enum handling

This commit is contained in:
Steve Ebersole 2023-03-14 09:14:08 -05:00
parent 8ee3be020b
commit 882e5d7622
28 changed files with 483 additions and 376 deletions

View File

@ -10,6 +10,10 @@ plugins {
id 'org.hibernate.build.xjc-jakarta'
}
repositories {
gradlePluginPortal()
}
description = 'Hibernate\'s core ORM functionality'
apply from: rootProject.file( 'gradle/published-java-module.gradle' )

View File

@ -6,66 +6,104 @@
*/
package org.hibernate.boot.model.process.internal;
import java.util.Locale;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.BasicValue;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.EnumType;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TINYINT;
/**
* Resolution for {@linkplain Enum enum} mappings using {@link jakarta.persistence.Enumerated},
* either implicitly or explicitly
*
* @author Steve Ebersole
*/
public class EnumeratedValueResolution<E extends Enum<E>> implements BasicValue.Resolution<E> {
private final CustomType<Object> enumTypeMapping;
private final JavaType<E> domainJtd;
private final JavaType<?> jdbcJtd;
private final JdbcType jdbcType;
private final EnumValueConverter<E,?> valueConverter;
public class EnumeratedValueResolution<E extends Enum<E>,R> implements BasicValue.Resolution<E> {
public static final String PREFIX = "enum::";
private final EnumValueConverter<E,R> valueConverter;
private final ConvertedBasicType<E> jdbcMapping;
public EnumeratedValueResolution(
CustomType<Object> enumTypeMapping,
JavaType<E> domainJtd,
JavaType<?> jdbcJtd,
JdbcType jdbcType,
EnumValueConverter<E, ?> valueConverter) {
this.enumTypeMapping = enumTypeMapping;
this.domainJtd = domainJtd;
this.jdbcJtd = jdbcJtd;
this.jdbcType = jdbcType;
EnumValueConverter<E, R> valueConverter,
MetadataBuildingContext context) {
this.valueConverter = valueConverter;
final String externalizableName = createName( valueConverter );
this.jdbcMapping = new ConvertedBasicTypeImpl<>( externalizableName, jdbcType, valueConverter );
// todo (enum) : register database objects if needed
}
private String createName(EnumValueConverter<E, R> valueConverter) {
return String.format(
Locale.ROOT,
PREFIX + "%s::%s",
valueConverter.getDomainJavaType().getJavaType().getName(),
enumStyle( valueConverter ).name()
);
}
private static EnumType enumStyle(EnumValueConverter<?,?> valueConverter) {
if ( valueConverter instanceof NamedEnumValueConverter ) {
return EnumType.STRING;
}
else if ( valueConverter instanceof OrdinalEnumValueConverter ) {
return EnumType.ORDINAL;
}
throw new UnsupportedOperationException();
}
@Override
public JdbcMapping getJdbcMapping() {
return enumTypeMapping;
public ConvertedBasicType<E> getJdbcMapping() {
return jdbcMapping;
}
@Override
public BasicType getLegacyResolvedBasicType() {
return enumTypeMapping;
public ConvertedBasicType<E> getLegacyResolvedBasicType() {
return jdbcMapping;
}
@Override
public JavaType<E> getDomainJavaType() {
return domainJtd;
return jdbcMapping.getJavaTypeDescriptor();
}
@Override
public JavaType<?> getRelationalJavaType() {
return jdbcJtd;
return jdbcMapping.getJdbcJavaType();
}
@Override
public JdbcType getJdbcType() {
return jdbcType;
return jdbcMapping.getJdbcType();
}
@Override
public EnumValueConverter getValueConverter() {
public EnumValueConverter<E,R> getValueConverter() {
return valueConverter;
}
@ -73,4 +111,60 @@ public class EnumeratedValueResolution<E extends Enum<E>> implements BasicValue.
public MutabilityPlan<E> getMutabilityPlan() {
return ImmutableMutabilityPlan.instance();
}
public static <E extends Enum<E>> EnumeratedValueResolution<E,?> fromName(
String name,
JdbcTypeIndicators jdbcTypeIndicators,
MetadataBuildingContext context) {
assert name != null;
assert name.startsWith( PREFIX );
final String[] parts = StringHelper.split( "::", name );
assert parts.length == 3;
assert "enum".equals( parts[0] );
final TypeConfiguration typeConfiguration = context.getBootstrapContext().getTypeConfiguration();
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
final Class<E> enumClass = resolveEnumClass( parts[1], context.getBootstrapContext() );
final jakarta.persistence.EnumType style = jakarta.persistence.EnumType.valueOf( parts[ 2 ] );
//noinspection unchecked,rawtypes
final EnumJavaType<E> enumJavaType = (EnumJavaType) javaTypeRegistry.getDescriptor( enumClass );
final JdbcType jdbcType;
final EnumValueConverter<E,?> converter;
if ( style == EnumType.ORDINAL ) {
jdbcType = jdbcTypeRegistry.getDescriptor( enumJavaType.hasManyValues() ? SMALLINT : TINYINT );
final JavaType<Integer> jdbcJavaType = javaTypeRegistry.getDescriptor( Integer.class );
converter = new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, jdbcJavaType );
}
else if ( style == EnumType.STRING ) {
//noinspection rawtypes
final JavaType jdbcJavaType;
if ( jdbcTypeIndicators.getColumnLength() == 1 ) {
jdbcJavaType = javaTypeRegistry.getDescriptor( Character.class );
}
else {
jdbcJavaType = javaTypeRegistry.getDescriptor( String.class );
}
jdbcType = jdbcJavaType.getRecommendedJdbcType( jdbcTypeIndicators );
//noinspection unchecked,rawtypes
converter = new NamedEnumValueConverter<>( enumJavaType, jdbcType, jdbcJavaType );
}
else {
throw new IllegalArgumentException( );
}
return new EnumeratedValueResolution<>( jdbcType, converter, context );
}
private static <E extends Enum<E>> Class<E> resolveEnumClass(String enumClassName, BootstrapContext bootstrapContext) {
final ServiceRegistry serviceRegistry = bootstrapContext.getServiceRegistry();
final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
return classLoaderService.classForName( enumClassName );
}
}

View File

@ -12,6 +12,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
@ -20,14 +21,12 @@ import org.hibernate.mapping.Table;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.AdjustableBasicType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.SerializableType;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.JavaTypeHelper;
import org.hibernate.type.descriptor.java.MutabilityPlan;
@ -61,8 +60,10 @@ public class InferredBasicValueResolver {
Selectable selectable,
String ownerName,
String propertyName,
Dialect dialect,
TypeConfiguration typeConfiguration) {
MetadataBuildingContext buildingContext) {
final Dialect dialect = buildingContext.getMetadataCollector().getDatabase().getDialect();
final TypeConfiguration typeConfiguration = buildingContext.getBootstrapContext().getTypeConfiguration();
final JavaType<T> reflectedJtd = reflectedJtdResolver.get();
// NOTE : the distinction that is made below wrt `explicitJavaType` and `reflectedJtd` is
@ -79,7 +80,7 @@ public class InferredBasicValueResolver {
null,
explicitJdbcType,
stdIndicators,
typeConfiguration
buildingContext
);
}
else if ( JavaTypeHelper.isTemporal( explicitJavaType ) ) {
@ -131,7 +132,7 @@ public class InferredBasicValueResolver {
null,
explicitJdbcType,
stdIndicators,
typeConfiguration
buildingContext
);
}
else if ( JavaTypeHelper.isTemporal( reflectedJtd ) ) {
@ -162,14 +163,14 @@ public class InferredBasicValueResolver {
final JavaType<?> elementJtd = containerJtd.getElementJavaType();
final BasicType registeredElementType;
if ( elementJtd instanceof EnumJavaType ) {
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromEnum(
(EnumJavaType) elementJtd,
final EnumeratedValueResolution<?,?> resolution = fromEnum(
(EnumJavaType<?>) elementJtd,
null,
null,
stdIndicators,
typeConfiguration
buildingContext
);
registeredElementType = resolution.getLegacyResolvedBasicType();
registeredElementType = resolution.getJdbcMapping();
}
else if ( JavaTypeHelper.isTemporal( elementJtd ) ) {
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromTemporal(
@ -321,60 +322,80 @@ public class InferredBasicValueResolver {
}
}
public static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E,?> fromEnum(
public static <E extends Enum<E>, R> EnumeratedValueResolution<E,R> fromEnum(
EnumJavaType<E> enumJavaType,
BasicJavaType<R> explicitJavaType,
JdbcType explicitJdbcType,
JdbcTypeIndicators stdIndicators,
MetadataBuildingContext context) {
final EnumType enumStyle = stdIndicators.getEnumeratedType();
if ( enumStyle == EnumType.STRING ) {
//noinspection unchecked
return (EnumeratedValueResolution<E, R>) stringEnumValueResolution(
enumJavaType,
explicitJavaType,
explicitJdbcType,
stdIndicators,
context
);
}
if ( enumStyle == EnumType.ORDINAL ) {
//noinspection unchecked
return (EnumeratedValueResolution<E, R>) ordinalEnumValueResolution(
enumJavaType,
(BasicJavaType<? extends Number>)explicitJavaType,
explicitJdbcType,
context
);
}
if ( enumStyle == null ) {
// NOTE : separate from the explicit ORDINAL check to facilitate
// handling native database enum types. In theory anyway - atm
// we cannot discern an implicit (default value) or explicit style
// due to HCANN and annotation handling for default values
//noinspection unchecked
return (EnumeratedValueResolution<E, R>) ordinalEnumValueResolution(
enumJavaType,
(BasicJavaType<? extends Number>)explicitJavaType,
explicitJdbcType,
context
);
}
throw new MappingException( "Unknown enumeration-style (JPA EnumType) : " + enumStyle );
}
private static <E extends Enum<E>, N extends Number> EnumeratedValueResolution<E,N> ordinalEnumValueResolution(
EnumJavaType<E> enumJavaType,
BasicJavaType<N> explicitJavaType,
JdbcType explicitJdbcType,
JdbcTypeIndicators stdIndicators,
TypeConfiguration typeConfiguration) {
final EnumType enumStyle = stdIndicators.getEnumeratedType() != null
? stdIndicators.getEnumeratedType()
: EnumType.ORDINAL;
MetadataBuildingContext context) {
final JavaType<N> relationalJavaType = ordinalJavaType( explicitJavaType, context );
final JdbcType jdbcType = ordinalJdbcType( explicitJdbcType, enumJavaType, context );
switch ( enumStyle ) {
case STRING: {
return stringEnumValueResolution(
enumJavaType,
explicitJavaType,
explicitJdbcType,
stdIndicators,
typeConfiguration
);
}
case ORDINAL: {
return ordinalEnumValueResolution(
enumJavaType,
explicitJavaType,
explicitJdbcType,
typeConfiguration
);
}
default: {
throw new MappingException( "Unknown enumeration-style (JPA EnumType) : " + enumStyle );
}
}
}
private static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E,N> ordinalEnumValueResolution(
EnumJavaType<E> enumJavaType,
JavaType<N> explicitJavaType,
JdbcType explicitJdbcType,
TypeConfiguration typeConfiguration) {
return ordinalResolution(
enumJavaType,
ordinalJavaType( explicitJavaType, typeConfiguration ),
ordinalJdbcType( explicitJdbcType, enumJavaType, typeConfiguration ),
typeConfiguration
return new EnumeratedValueResolution<>(
jdbcType,
new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, relationalJavaType ),
context
);
}
private static JdbcType ordinalJdbcType(JdbcType explicitJdbcType, EnumJavaType<?> enumJavaType, TypeConfiguration typeConfiguration) {
private static JdbcType ordinalJdbcType(
JdbcType explicitJdbcType,
EnumJavaType<?> enumJavaType,
MetadataBuildingContext context) {
return explicitJdbcType != null
? explicitJdbcType
: typeConfiguration.getJdbcTypeRegistry().getDescriptor( enumJavaType.hasManyValues() ? SMALLINT : TINYINT );
: context.getMetadataCollector().getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( enumJavaType.hasManyValues() ? SMALLINT : TINYINT );
}
private static <N extends Number> JavaType<N> ordinalJavaType(JavaType<N> explicitJavaType, TypeConfiguration typeConfiguration) {
private static <N extends Number> JavaType<N> ordinalJavaType(
JavaType<N> explicitJavaType,
MetadataBuildingContext context) {
if ( explicitJavaType != null ) {
if ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
throw new MappingException(
@ -386,73 +407,25 @@ public class InferredBasicValueResolver {
return explicitJavaType;
}
else {
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class );
return context.getMetadataCollector().getTypeConfiguration().getJavaTypeRegistry().getDescriptor( Integer.class );
}
}
private static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E, N> ordinalResolution(
EnumJavaType<E> enumJavaType,
JavaType<N> relationalJtd,
JdbcType jdbcType,
TypeConfiguration typeConfiguration
) {
final CustomType<E> customType = new CustomType<>(
new org.hibernate.type.EnumType<>(
enumJavaType.getJavaTypeClass(),
new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, relationalJtd ),
typeConfiguration
),
typeConfiguration
);
return new InferredBasicValueResolution<>(
customType,
enumJavaType,
relationalJtd,
jdbcType,
customType,
ImmutableMutabilityPlan.instance()
);
}
private static <E extends Enum<E>> InferredBasicValueResolution<E, String> stringEnumValueResolution(
private static <E extends Enum<E>> EnumeratedValueResolution<E,String> stringEnumValueResolution(
EnumJavaType<E> enumJavaType,
BasicJavaType<?> explicitJavaType,
JdbcType explicitJdbcType,
JdbcTypeIndicators stdIndicators,
TypeConfiguration typeConfiguration) {
final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, typeConfiguration );
return stringResolution(
enumJavaType,
relationalJtd,
stringJdbcType( explicitJdbcType, stdIndicators, relationalJtd ),
typeConfiguration
);
}
MetadataBuildingContext context) {
final JdbcType jdbcType = explicitJdbcType == null
? enumJavaType.getRecommendedJdbcType( stdIndicators )
: explicitJdbcType;
final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, context );
private static <E extends Enum<E>> InferredBasicValueResolution<E, String> stringResolution(
EnumJavaType<E> enumJavaType,
JavaType<String> relationalJtd,
JdbcType jdbcType,
TypeConfiguration typeConfiguration) {
final CustomType<E> customType = new CustomType<>(
new org.hibernate.type.EnumType<>(
enumJavaType.getJavaTypeClass(),
new NamedEnumValueConverter<E>(
enumJavaType,
jdbcType,
relationalJtd
),
typeConfiguration
),
typeConfiguration
);
return new InferredBasicValueResolution<>(
customType,
enumJavaType,
relationalJtd,
return new EnumeratedValueResolution<>(
jdbcType,
customType,
ImmutableMutabilityPlan.instance()
new NamedEnumValueConverter<>( enumJavaType, jdbcType, relationalJtd ),
context
);
}
@ -462,7 +435,10 @@ public class InferredBasicValueResolver {
: relationalJtd.getRecommendedJdbcType( stdIndicators );
}
private static JavaType<String> stringJavaType(BasicJavaType<?> explicitJavaType, JdbcTypeIndicators stdIndicators, TypeConfiguration typeConfiguration) {
private static JavaType<String> stringJavaType(
BasicJavaType<?> explicitJavaType,
JdbcTypeIndicators stdIndicators,
MetadataBuildingContext context) {
if ( explicitJavaType != null ) {
if ( ! String.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
throw new MappingException(
@ -474,7 +450,7 @@ public class InferredBasicValueResolver {
return (JavaType<String>) explicitJavaType;
}
else {
return typeConfiguration.getJavaTypeRegistry()
return context.getMetadataCollector().getTypeConfiguration().getJavaTypeRegistry()
.getDescriptor( stdIndicators.getColumnLength() == 1 ? Character.class : String.class );
}
}

View File

@ -112,19 +112,22 @@ public class SqlStatementLogger {
*/
@AllowSysOut
public void logStatement(String statement, Formatter formatter) {
if ( logToStdout || LOG.isDebugEnabled() ) {
try {
if ( format ) {
statement = formatter.format( statement );
}
if ( highlight ) {
statement = FormatStyle.HIGHLIGHT.getFormatter().format( statement );
}
if ( !logToStdout && !LOG.isDebugEnabled() ) {
return;
}
try {
if ( format ) {
statement = formatter.format( statement );
}
catch (RuntimeException ex) {
LOG.warn( "Couldn't format statement", ex );
if ( highlight ) {
statement = FormatStyle.HIGHLIGHT.getFormatter().format( statement );
}
}
catch (RuntimeException ex) {
LOG.warn( "Couldn't format statement", ex );
}
LOG.debug( statement );
if ( logToStdout ) {
String prefix = highlight ? "\u001b[35m[Hibernate]\u001b[0m " : "Hibernate: ";

View File

@ -20,6 +20,7 @@ import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
import org.hibernate.boot.model.process.internal.EnumeratedValueResolution;
import org.hibernate.boot.model.process.internal.InferredBasicValueResolution;
import org.hibernate.boot.model.process.internal.InferredBasicValueResolver;
import org.hibernate.boot.model.process.internal.NamedBasicTypeResolution;
@ -485,8 +486,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
column,
ownerName,
propertyName,
getDialect(),
getTypeConfiguration()
getBuildingContext()
);
}
}
@ -569,7 +569,6 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
}
};
// Name could refer to:
// 1) a named converter - HBM support for JPA's AttributeConverter via its `type="..."` XML attribute
// 2) a "named composed" mapping - like (1), this is mainly to support envers since it tells
@ -589,6 +588,10 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
);
}
if ( name.startsWith( EnumeratedValueResolution.PREFIX ) ) {
return EnumeratedValueResolution.fromName( name, stdIndicators, context );
}
if ( name.startsWith( BasicTypeImpl.EXTERNALIZED_PREFIX ) ) {
final BasicTypeImpl<Object> basicType = context.getBootstrapContext().resolveAdHocBasicType( name );

View File

@ -67,10 +67,6 @@ public interface JdbcMapping extends MappingType, JdbcMappingContainer {
*/
JdbcType getJdbcType();
default CastType getCastType() {
return getJdbcType().getCastType();
}
/**
* The strategy for extracting values of this expressible
* type from JDBC ResultSets, CallableStatements, etc
@ -83,6 +79,10 @@ public interface JdbcMapping extends MappingType, JdbcMappingContainer {
*/
ValueBinder getJdbcValueBinder();
default CastType getCastType() {
return getJdbcType().getCastType();
}
/**
* The strategy for formatting values of this expressible type to
* a SQL literal.

View File

@ -17,8 +17,6 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.hibernate.boot.model.NamedEntityGraphDefinition;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
@ -47,11 +45,11 @@ import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.DynamicModelJavaType;
import org.hibernate.type.descriptor.java.spi.EntityJavaType;
@ -584,40 +582,27 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
}
}
final Consumer<PersistentAttribute<?, ?>> attributeConsumer = persistentAttribute -> {
if ( persistentAttribute.getJavaType() != null && persistentAttribute.getJavaType().isEnum() ) {
@SuppressWarnings("unchecked")
final Class<Enum<?>> enumClass = (Class<Enum<?>>) persistentAttribute.getJavaType();
final Enum<?>[] enumConstants = enumClass.getEnumConstants();
typeConfiguration.getJavaTypeRegistry().forEachDescriptor( (descriptor) -> {
if ( descriptor instanceof EnumJavaType ) {
final EnumJavaType<? extends Enum<?>> enumJavaType = (EnumJavaType<? extends Enum<?>>) descriptor;
final Class<? extends Enum<?>> enumJavaClass = enumJavaType.getJavaTypeClass();
final Enum<?>[] enumConstants = enumJavaClass.getEnumConstants();
for ( Enum<?> enumConstant : enumConstants ) {
final String qualifiedEnumLiteral = enumConstant.getDeclaringClass()
.getSimpleName() + "." + enumConstant.name();
allowedEnumLiteralTexts
.computeIfAbsent( enumConstant.name(), (s) -> new HashMap<>() )
.put( enumJavaClass, enumConstant );
this.allowedEnumLiteralTexts.computeIfAbsent(
enumConstant.name(),
k -> new HashMap<>()
).put( enumClass, enumConstant );
this.allowedEnumLiteralTexts.computeIfAbsent(
qualifiedEnumLiteral,
k -> new HashMap<>()
).put( enumClass, enumConstant );
final String simpleQualifiedName = enumJavaClass.getSimpleName() + "." + enumConstant.name();
allowedEnumLiteralTexts
.computeIfAbsent( simpleQualifiedName, (s) -> new HashMap<>() )
.put( enumJavaClass, enumConstant );
}
}
};
domainTypeStream( context ).forEach(
managedDomainType -> managedDomainType.visitAttributes( attributeConsumer )
);
} );
applyNamedEntityGraphs( namedEntityGraphDefinitions );
}
private static Stream<ManagedDomainType<?>> domainTypeStream(MetadataContext context) {
return Stream.concat(
context.getIdentifiableTypesByName().values().stream(),
context.getEmbeddableTypeSet().stream()
);
}
private EntityDomainType<?> locateOrBuildEntityType(
PersistentClass persistentClass,
MetadataContext context,

View File

@ -49,7 +49,10 @@ import org.jboss.logging.Logger;
* @author Emmanuel Bernard
* @author Hardy Ferentschik
* @author Steve Ebersole
*
* @deprecated Use {@link ConvertedBasicType} instead
*/
@Deprecated(since="6.2", forRemoval=true)
public class EnumType<T extends Enum<T>>
implements EnhancedUserType<T>, DynamicParameterizedType, LoggableUserType, TypeConfigurationAware, Serializable {
private static final Logger LOG = CoreLogging.logger( EnumType.class );

View File

@ -13,6 +13,11 @@ import java.sql.SQLException;
/**
* Contract for binding values to a JDBC {@link PreparedStatement}.
*
* @apiNote Binders, as well as {@linkplain ValueExtractor extractors}, should never apply
* {@linkplain org.hibernate.type.descriptor.converter.spi.BasicValueConverter conversions}.
* Instead, callers of the binder are expected to coordinate between the binding and
* conversion.
*
* @author Steve Ebersole
*/
public interface ValueBinder<X> {

View File

@ -14,6 +14,11 @@ import java.sql.SQLException;
* Contract for extracting values from a JDBC {@link ResultSet} or
* from output the parameters of a {@link CallableStatement}.
*
* @apiNote Extractors, as well as {@linkplain ValueBinder binders}, should never apply
* {@linkplain org.hibernate.type.descriptor.converter.spi.BasicValueConverter conversions}.
* Instead, callers of the extractor are expected to coordinate between the extraction and
* conversion.
*
* @author Steve Ebersole
*/
public interface ValueExtractor<X> {

View File

@ -37,9 +37,10 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
final JdbcTypeRegistry registry = context.getTypeConfiguration().getJdbcTypeRegistry();
final EnumType type = context.getEnumeratedType();
switch ( type == null ? ORDINAL : type ) {
case ORDINAL:
case ORDINAL: {
return registry.getDescriptor( hasManyValues() ? SMALLINT : TINYINT );
case STRING:
}
case STRING: {
if ( context.getColumnLength() == 1 ) {
return context.isNationalized()
? registry.getDescriptor( NCHAR )
@ -48,8 +49,10 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
return context.isNationalized()
? registry.getDescriptor( NVARCHAR )
: registry.getDescriptor( VARCHAR );
default:
}
default: {
throw new AssertionFailure("unknown EnumType");
}
}
}

View File

@ -10,6 +10,7 @@ import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.type.descriptor.java.ArrayJavaType;
@ -70,35 +71,12 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// descriptor access
public void forEachDescriptor(Consumer<JavaType<?>> consumer) {
descriptorsByType.values().forEach( consumer );
}
public <T> JavaType<T> getDescriptor(Type javaType) {
return resolveDescriptor( javaType );
// return RegistryHelper.INSTANCE.resolveDescriptor(
// descriptorsByClass,
// javaType,
// () -> {
// log.debugf(
// "Could not find matching scoped JavaType for requested Java class [%s]; " +
// "falling back to static registry",
// javaType.getName()
// );
//
// if ( Serializable.class.isAssignableFrom( javaType ) ) {
// return new SerializableTypeDescriptor( javaType );
// }
//
// if ( !AttributeConverter.class.isAssignableFrom( javaType ) ) {
// log.debugf(
// "Could not find matching JavaType for requested Java class [%s]; using fallback. " +
// "This means Hibernate does not know how to perform certain basic operations in relation to this Java type." +
// "",
// javaType.getName()
// );
// checkEqualsAndHashCode( javaType );
// }
//
// return new FallbackJavaType<>( javaType );
// }
// );
}
public void addDescriptor(JavaType<?> descriptor) {
@ -115,6 +93,7 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
}
public <J> JavaType<J> findDescriptor(Type javaType) {
//noinspection unchecked
return (JavaType<J>) descriptorsByType.get( javaType );
}
@ -200,25 +179,19 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
final ParameterizedType parameterizedType = (ParameterizedType) javaType;
javaTypeClass = (Class<J>) parameterizedType.getRawType();
}
final MutabilityPlan<J> mutabilityPlan;
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan(
javaType,
typeConfiguration
);
if ( determinedPlan != null ) {
mutabilityPlan = determinedPlan;
}
else {
mutabilityPlan = (MutabilityPlan<J>) MutableMutabilityPlan.INSTANCE;
}
return entity ? new EntityJavaType<>( javaTypeClass, mutabilityPlan )
final MutabilityPlan<J> mutabilityPlan = (determinedPlan != null)
? determinedPlan
: (MutabilityPlan<J>) MutableMutabilityPlan.INSTANCE;
return entity
? new EntityJavaType<>( javaTypeClass, mutabilityPlan )
: new JavaTypeBasicAdaptor<>( javaTypeClass, mutabilityPlan );
}
);
}
public JavaType<?> resolveDynamicEntityDescriptor(String typeName) {
return new DynamicModelJavaType();
}
}

View File

@ -20,7 +20,6 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.type.CustomType;
import org.hibernate.type.spi.TypeConfiguration;
import org.junit.jupiter.api.Test;
@ -56,8 +55,7 @@ public class NestedEmbeddableMetadataTest {
assertEquals( (Long) 500L, selectable.getLength() );
Component amountMetadata = (Component) investmentMetadata.getProperty( "amount" ).getValue();
SimpleValue currencyMetadata = (SimpleValue) amountMetadata.getProperty( "currency" ).getValue();
CustomType<Object> currencyType = (CustomType<Object>) currencyMetadata.getType();
int[] currencySqlTypes = currencyType.getSqlTypeCodes( metadata );
int[] currencySqlTypes = currencyMetadata.getType().getSqlTypeCodes( metadata );
assertEquals( 1, currencySqlTypes.length );
assertJdbcTypeCode(
typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARCHAR ).getJdbcTypeCode(),

View File

@ -19,11 +19,8 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.junit.jupiter.api.Test;
import static org.hibernate.testing.junit4.ExtraAssertions.assertJdbcTypeCode;
@ -54,8 +51,7 @@ public class FieldAccessedNestedEmbeddableMetadataTest {
assertEquals( (Long) 500L, selectable.getLength() );
Component amountMetadata = (Component) investmentMetadata.getProperty( "amount" ).getValue();
SimpleValue currencyMetadata = (SimpleValue) amountMetadata.getProperty( "currency" ).getValue();
CustomType<Object> currencyType = (CustomType<Object>) currencyMetadata.getType();
int[] currencySqlTypes = currencyType.getSqlTypeCodes( metadata );
int[] currencySqlTypes = currencyMetadata.getType().getSqlTypeCodes( metadata );
assertEquals( 1, currencySqlTypes.length );
assertJdbcTypeCode(
typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARCHAR ).getJdbcTypeCode(),

View File

@ -7,25 +7,30 @@
package org.hibernate.orm.test.annotations.enumerated;
import java.sql.Types;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.assertj.core.api.Assert;
import org.assertj.core.api.Assertions;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.core.Is.is;
@ -34,29 +39,16 @@ import static org.junit.Assert.assertThat;
/**
* @author Steve Ebersole
*/
public class EnumeratedSmokeTest extends BaseUnitTestCase {
private StandardServiceRegistry ssr;
@Before
public void prepare() {
ssr = new StandardServiceRegistryBuilder().build();
}
@After
public void release() {
if ( ssr != null ) {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@ServiceRegistry
public class EnumeratedSmokeTest {
/**
* I personally have been unable to repeoduce the bug as reported in HHH-10402. This test
* is equivalent to what the reporters say happens, but these tests pass fine.
*/
@Test
@TestForIssue( jiraKey = "HHH-10402" )
public void testEnumeratedTypeResolutions() {
final MetadataImplementor mappings = (MetadataImplementor) new MetadataSources( ssr )
@JiraKey( "HHH-10402" )
public void testEnumeratedTypeResolutions(ServiceRegistryScope serviceRegistryScope) {
final MetadataImplementor mappings = (MetadataImplementor) new MetadataSources( serviceRegistryScope.getRegistry() )
.addAnnotatedClass( EntityWithEnumeratedAttributes.class )
.buildMetadata();
mappings.orderColumns( false );
@ -72,20 +64,20 @@ public class EnumeratedSmokeTest extends BaseUnitTestCase {
}
private void validateEnumMapping(JdbcTypeRegistry jdbcRegistry, Property property, EnumType expectedJpaEnumType) {
assertThat( property.getType(), instanceOf( CustomType.class ) );
final CustomType<Object> customType = (CustomType<Object>) property.getType();
assertThat( customType.getUserType(), instanceOf( org.hibernate.type.EnumType.class ) );
final org.hibernate.type.EnumType hibernateMappingEnumType = (org.hibernate.type.EnumType) customType.getUserType();
assertThat( hibernateMappingEnumType.isOrdinal(), is(expectedJpaEnumType==EnumType.ORDINAL) );
final int expectedJdbcTypeCode = jdbcRegistry.getDescriptor(
expectedJpaEnumType == EnumType.ORDINAL ?
Types.TINYINT :
Types.VARCHAR
).getJdbcTypeCode();
assertThat(
hibernateMappingEnumType.getSqlType(),
is( expectedJdbcTypeCode )
);
final ConvertedBasicType<?> propertyType = (ConvertedBasicType<?>) property.getType();
final EnumValueConverter<?, ?> valueConverter = (EnumValueConverter<?, ?>) propertyType.getValueConverter();
final JdbcMapping jdbcMapping = propertyType.getJdbcMapping();
final JdbcType jdbcType = jdbcMapping.getJdbcType();
assert expectedJpaEnumType != null;
if ( expectedJpaEnumType == EnumType.ORDINAL ) {
Assertions.assertThat( valueConverter ).isInstanceOf( OrdinalEnumValueConverter.class );
Assertions.assertThat( jdbcType.isInteger() ).isTrue();
}
else {
Assertions.assertThat( valueConverter ).isInstanceOf( NamedEnumValueConverter.class );
Assertions.assertThat( jdbcType.isString() ).isTrue();
}
}
@Entity

View File

@ -26,6 +26,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType;
@ -73,19 +74,17 @@ public class EnumeratedWithMappedSuperclassTest extends BaseUnitTestCase {
final PersistentClass addressLevelBinding = metadata.getEntityBinding( AddressLevel.class.getName() );
final Property natureProperty = addressLevelBinding.getProperty( "nature" );
CustomType<Object> customType = assertTyping( CustomType.class, natureProperty.getType() );
EnumType enumType = assertTyping( EnumType.class, customType.getUserType() );
assertEquals( Types.VARCHAR, enumType.getSqlType() );
//noinspection unchecked
ConvertedBasicType<Nature> natureMapping = (ConvertedBasicType<Nature>) natureProperty.getType();
assertEquals( Types.VARCHAR, natureMapping.getJdbcType().getJdbcTypeCode() );
SessionFactoryImplementor sf = (SessionFactoryImplementor) metadata.buildSessionFactory();
try {
EntityPersister p = sf.getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(AddressLevel.class.getName());
CustomType<Object> runtimeType = assertTyping( CustomType.class, p.getPropertyType( "nature" ) );
EnumType runtimeEnumType = assertTyping( EnumType.class, runtimeType.getUserType() );
assertEquals( Types.VARCHAR, runtimeEnumType.getSqlType() );
}
finally {
sf.close();
try ( SessionFactoryImplementor sf = (SessionFactoryImplementor) metadata.buildSessionFactory() ) {
EntityPersister p = sf.getRuntimeMetamodels()
.getMappingMetamodel()
.getEntityDescriptor( AddressLevel.class.getName() );
//noinspection unchecked
ConvertedBasicType<Nature> runtimeType = (ConvertedBasicType<Nature>) p.getPropertyType( "nature" );
assertEquals( Types.VARCHAR, runtimeType.getJdbcType().getJdbcTypeCode() );
}
}

View File

@ -6,9 +6,12 @@
*/
package org.hibernate.orm.test.annotations.enumerated.ormXml;
import java.sql.Types;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType;
import org.hibernate.type.Type;
@ -19,6 +22,7 @@ import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/**
@ -39,9 +43,9 @@ public class OrmXmlEnumTypeTest extends BaseUnitTestCase {
Type bindingPropertyType = metadata.getEntityBinding( BookWithOrmEnum.class.getName() )
.getProperty( "bindingStringEnum" )
.getType();
CustomType<Object> customType = ExtraAssertions.assertTyping( CustomType.class, bindingPropertyType );
EnumType enumType = ExtraAssertions.assertTyping( EnumType.class, customType.getUserType() );
assertFalse( enumType.isOrdinal() );
ConvertedBasicType<?> enumMapping = ExtraAssertions.assertTyping( ConvertedBasicType.class, bindingPropertyType );
assertEquals( Types.VARCHAR, enumMapping.getJdbcType().getJdbcTypeCode() );
}
finally {
ServiceRegistryBuilder.destroy( ssr );

View File

@ -8,12 +8,6 @@ package org.hibernate.orm.test.bootstrap.binding.annotations.basics;
import java.sql.Types;
import java.util.function.Consumer;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.mapping.BasicValue;
@ -24,19 +18,23 @@ import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.usertype.UserType;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.junit.jupiter.api.Test;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.assertj.core.api.Assertions;
import static jakarta.persistence.EnumType.ORDINAL;
import static jakarta.persistence.EnumType.STRING;
import static org.hamcrest.CoreMatchers.equalTo;
@ -128,9 +126,6 @@ public class EnumResolutionTests {
//noinspection rawtypes
final Class converterType = ( (JpaAttributeConverter) converter ).getConverterBean().getBeanClass();
assertThat( converterType, equalTo( ConverterImpl.class ) );
},
(legacyResolution) -> {
assertThat( legacyResolution, instanceOf( ConvertedBasicTypeImpl.class ) );
}
);
}
@ -150,6 +145,22 @@ public class EnumResolutionTests {
);
}
@Test
public void testSingleCharEnumResolution(DomainModelScope scope) {
final PersistentClass entityBinding = scope
.getDomainModel()
.getEntityBinding( EntityWithEnums.class.getName() );
verifyEnumResolution(
entityBinding.getProperty( "singleCharEnum" ),
Types.CHAR,
Character.class,
(converter) -> {
Assertions.assertThat( converter.getRelationalJavaType().getJavaTypeClass() ).isEqualTo( Character.class );
}
);
}
@SuppressWarnings("rawtypes")
private void verifyEnumResolution(
Property property,
@ -164,13 +175,6 @@ public class EnumResolutionTests {
valueConverter -> {
assertThat( valueConverter, notNullValue() );
assertThat( valueConverter, instanceOf( converterClass ) );
},
legacyResolvedType -> {
assertThat( legacyResolvedType, instanceOf( CustomType.class ) );
final UserType rawEnumUserType = ( (CustomType<Object>) legacyResolvedType ).getUserType();
assertThat( rawEnumUserType, instanceOf( EnumType.class ) );
final EnumType rawEnumEnumType = (EnumType) rawEnumUserType;
assertThat( rawEnumEnumType.isOrdinal(), is( isOrdinal ) );
}
);
}
@ -180,11 +184,11 @@ public class EnumResolutionTests {
Property property,
int jdbcCode,
Class<?> javaType,
Consumer<BasicValueConverter> converterChecker,
Consumer<BasicType> legacyTypeChecker) {
Consumer<BasicValueConverter> converterChecker) {
final BasicValue.Resolution<?> resolution = ( (BasicValue) property.getValue() ).resolve();
final TypeConfiguration typeConfiguration = ( (BasicValue) property.getValue() ).getTypeConfiguration();
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( jdbcCode );
// verify the interpretations used for reading
assertThat( resolution.getJdbcType(), is( jdbcType ) );
assertThat( resolution.getRelationalJavaType().getJavaTypeClass(), equalTo( javaType ) );
@ -195,9 +199,6 @@ public class EnumResolutionTests {
assertThat( jdbcMapping.getJdbcJavaType(), equalTo( resolution.getRelationalJavaType() ) );
converterChecker.accept( resolution.getValueConverter() );
// verify the (legacy) interpretations used for writing
legacyTypeChecker.accept( resolution.getLegacyResolvedBasicType() );
}
@Entity( name = "EntityWithEnums" )
@ -224,6 +225,10 @@ public class EnumResolutionTests {
@Enumerated( ORDINAL )
@JdbcTypeCode( Types.SMALLINT )
private Values explicitEnum;
@Enumerated( STRING )
@Column( length = 1 )
private Values singleCharEnum;
}
enum Values { FIRST, SECOND }

View File

@ -6,8 +6,11 @@
*/
package org.hibernate.orm.test.bootstrap.spi.delegation;
import java.util.Set;
import org.hibernate.boot.spi.AbstractDelegatingMetadata;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.type.ConvertedBasicType;
/**
* If this class does not compile anymore due to unimplemented methods, you should probably add the corresponding

View File

@ -25,14 +25,13 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.testing.orm.junit.DomainModel;
@ -92,9 +91,8 @@ public class SmokeTests {
assertThat( genderAttrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) );
final CustomType<?> basicType = (CustomType<?>) genderAttrMapping.getJdbcMapping();
final org.hibernate.type.EnumType<?> enumType = (org.hibernate.type.EnumType<?>) basicType.getUserType();
final EnumValueConverter<?, ?> valueConverter = enumType.getEnumValueConverter();
final ConvertedBasicType<?> jdbcMapping = (ConvertedBasicType<?>) genderAttrMapping.getJdbcMapping();
final EnumValueConverter<?, ?> valueConverter = (EnumValueConverter<?, ?>) jdbcMapping.getValueConverter();
assertThat( valueConverter, instanceOf( OrdinalEnumValueConverter.class ) );
assertThat(
valueConverter.getDomainJavaType().getJavaTypeClass(),
@ -117,9 +115,8 @@ public class SmokeTests {
assertThat( attrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) );
final CustomType<?> basicType = (CustomType<?>) attrMapping.getJdbcMapping();
final org.hibernate.type.EnumType<?> enumType = (org.hibernate.type.EnumType<?>) basicType.getUserType();
final EnumValueConverter<?, ?> valueConverter = enumType.getEnumValueConverter();
final ConvertedBasicType<?> jdbcMapping = (ConvertedBasicType<?>) attrMapping.getJdbcMapping();
final EnumValueConverter<?, ?> valueConverter = (EnumValueConverter<?, ?>) jdbcMapping.getValueConverter();
assertThat( valueConverter, instanceOf( NamedEnumValueConverter.class ) );
assertThat(
valueConverter.getDomainJavaType().getJavaTypeClass(),
@ -142,7 +139,7 @@ public class SmokeTests {
assertThat( attrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) );
final BasicValueConverter valueConverter = ( (BasicType<?>) attrMapping.getJdbcMapping() ).getValueConverter();
final BasicValueConverter<?,?> valueConverter = ( (ConvertedBasicType<?>) attrMapping.getJdbcMapping() ).getValueConverter();
assertThat( valueConverter, instanceOf( JpaAttributeConverter.class ) );
assertThat( valueConverter.getDomainJavaType(), is( attrMapping.getJavaType() ) );
assertThat( valueConverter.getRelationalJavaType().getJavaTypeClass(), equalTo( Character.class ) );

View File

@ -9,6 +9,7 @@ package org.hibernate.orm.test.sql.ast;
import java.sql.Types;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
@ -39,6 +40,7 @@ import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.testing.hamcrest.AssignableMatcher;
@ -117,9 +119,6 @@ public class SmokeTests {
public void testConvertedHqlInterpretation(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final JdbcTypeRegistry jdbcTypeRegistry = session.getFactory()
.getTypeConfiguration()
.getJdbcTypeRegistry();
final QueryImplementor<Gender> query = session.createQuery( "select e.gender from SimpleEntity e", Gender.class );
final SqmQueryImplementor<Gender> hqlQuery = (SqmQueryImplementor<Gender>) query;
final SqmSelectStatement<Gender> sqmStatement = (SqmSelectStatement<Gender>) hqlQuery.getSqmStatement();
@ -167,17 +166,11 @@ public class SmokeTests {
final ColumnReference columnReference = (ColumnReference) selectedExpression;
assertThat( columnReference.getExpressionText(), is( "s1_0.gender" ) );
final JdbcMappingContainer selectedExpressible = selectedExpression.getExpressionType();
assertThat( selectedExpressible, instanceOf( CustomType.class ) );
final CustomType<?> basicType = (CustomType<?>) selectedExpressible;
final EnumType<?> enumType = (EnumType<?>) basicType.getUserType();
final EnumValueConverter<?, ?> enumConverter = enumType.getEnumValueConverter();
assertThat( enumConverter.getRelationalJavaType().getJavaTypeClass(), AssignableMatcher.assignableTo( Integer.class ) );
assertThat(
basicType.getJdbcType(),
is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) )
);
final JdbcMapping selectedExpressible = selectedExpression.getExpressionType().getSingleJdbcMapping();
assertThat( selectedExpressible.getJdbcType().isInteger(), is( true ) );
final EnumValueConverter<?, ?> enumConverter = (EnumValueConverter<?, ?>) selectedExpressible.getValueConverter();
assertThat( enumConverter.getRelationalJavaType().getJavaTypeClass(), AssignableMatcher.assignableTo( Integer.class ) );
assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) );
final DomainResult<?> domainResult = sqlAst.getDomainResultDescriptors().get( 0 );

View File

@ -41,6 +41,7 @@ import static org.junit.Assert.assertThat;
* @author Christian Beikov
*/
@SkipForDialect(value = SybaseASEDialect.class, comment = "Sybase or the driver are trimming trailing zeros in byte arrays")
@SkipForDialect( value = OracleDialect.class, jiraKey = "HHH-16333", comment = "converters not handled properly" )
public class EnumArrayTest extends BaseNonConfigCoreFunctionalTestCase {
@Override

View File

@ -16,9 +16,13 @@ import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.type.BasicType;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
/**
* Generates metadata for basic properties: immutable types (including enums).
@ -71,7 +75,10 @@ public final class BasicMetadataGenerator {
typeDefinition.setParameter( EnumType.NAMED, parameters.getProperty( EnumType.NAMED ) );
}
else {
typeDefinition.setParameter( EnumType.NAMED, "" + !( (EnumType) ( (CustomType<Object>) type ).getUserType() ).isOrdinal() );
final ConvertedBasicType<?> convertedType = (ConvertedBasicType<?>) type;
final EnumValueConverter<?, ?> valueConverter = (EnumValueConverter<?, ?>) convertedType.getValueConverter();
final boolean isNamed = valueConverter instanceof NamedEnumValueConverter;
typeDefinition.setParameter( EnumType.NAMED, Boolean.toString( isNamed ) );
}
}
@ -126,15 +133,10 @@ public final class BasicMetadataGenerator {
}
private boolean isEnumType(Type type, String typeName) {
// Check if a custom type implementation is used and it extends the EnumType directly.
if ( type instanceof CustomType ) {
final CustomType<?> customType = (CustomType<?>) type;
if ( customType.getUserType() instanceof EnumType ) {
return true;
}
if ( type instanceof ConvertedBasicType ) {
final ConvertedBasicType<?> convertedType = (ConvertedBasicType<?>) type;
return convertedType.getValueConverter() instanceof EnumValueConverter;
}
// Check if it is an EnumType without a custom type
return EnumType.class.getName().equals( typeName );
return false;
}
}

View File

@ -6,11 +6,13 @@
*/
package org.hibernate.orm.test.envers.entities.customtype;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SqlResultSetMapping;
import org.hibernate.envers.Audited;
@ -19,6 +21,13 @@ import org.hibernate.envers.Audited;
*/
@Entity
@Audited
@SqlResultSetMapping(
name = "e1_e2",
columns = {
@ColumnResult( name = "enum1", type = String.class ),
@ColumnResult( name = "enum2", type = Integer.class )
}
)
public class EnumTypeEntity {
public static enum E1 {X, Y}

View File

@ -9,18 +9,35 @@ package org.hibernate.orm.test.envers.integration.collection;
import java.util.Arrays;
import java.util.List;
import jakarta.persistence.EntityManager;
import org.assertj.core.api.Assertions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase;
import org.hibernate.orm.test.envers.Priority;
import org.hibernate.orm.test.envers.entities.collection.EnumSetEntity;
import org.hibernate.orm.test.envers.entities.collection.EnumSetEntity.E1;
import org.hibernate.orm.test.envers.entities.collection.EnumSetEntity.E2;
import org.hibernate.orm.test.envers.tools.TestTools;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Adam Warski (adam at warski dot org)
*/
@ -98,19 +115,48 @@ public class EnumSet extends BaseEnversJPAFunctionalTestCase {
@TestForIssue(jiraKey = "HHH-7780")
public void testEnumRepresentation() {
EntityManager entityManager = getEntityManager();
List<Object> enums1 = entityManager.createNativeQuery(
"SELECT enums1 FROM EnumSetEntity_enums1_AUD ORDER BY REV ASC"
).getResultList();
List<Object> enums2 = entityManager.createNativeQuery(
"SELECT enums2 FROM EnumSetEntity_enums2_AUD ORDER BY REV ASC"
).getResultList();
verifyModel( entityManager );
{
final String qry = "SELECT enums1 FROM EnumSetEntity_enums1_AUD ORDER BY REV ASC";
List<String> enums1 = entityManager.createNativeQuery( qry, String.class ).getResultList();
Assertions.assertThat( enums1 ).isEqualTo( List.of( "X", "Y", "X" ) );
}
{
final String qry = "SELECT enums2 FROM EnumSetEntity_enums2_AUD ORDER BY REV ASC";
String enum2 = (String) entityManager.createNativeQuery( qry, String.class ).getSingleResult();
// Compare the String value to account for, as an example, Oracle returning a BigDecimal instead of an int.
Assertions.assertThat( enum2 ).isEqualTo( "0" );
}
entityManager.close();
}
Assert.assertEquals( Arrays.asList( "X", "Y", "X" ), enums1 );
private void verifyModel(EntityManager entityManager) {
final MappingMetamodelImplementor mappingMetamodel = entityManager.unwrap( SessionImplementor.class )
.getFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
Assert.assertEquals( 1, enums2.size() );
Object enum2 = enums2.get( 0 );
// Compare the Strings to account for, as an example, Oracle returning a BigDecimal instead of an int.
Assert.assertEquals( "0", enum2.toString() );
{
final EntityMappingType entityMapping = mappingMetamodel.getEntityDescriptor( EnumSetEntity.class );
final PluralAttributeMapping attributeMapping = (PluralAttributeMapping) entityMapping.findDeclaredAttributeMapping( "enums1" );
verifyMapping( attributeMapping.getElementDescriptor().getJdbcMapping( 0 ) );
}
{
final EntityMappingType entityMapping = mappingMetamodel.getEntityDescriptor( "EnumSetEntity_enums1_AUD" );
final CompositeIdentifierMapping cidMapping = (CompositeIdentifierMapping) entityMapping.getIdentifierMapping();
verifyMapping( cidMapping.getEmbeddableTypeDescriptor().findAttributeMapping( "element" ).getJdbcMapping( 0 ) );
}
}
private void verifyMapping(JdbcMapping jdbcMapping) {
final ConvertedBasicType<?> convertedBasicType = (ConvertedBasicType<?>) jdbcMapping;
assertThat( convertedBasicType.getValueConverter() ).isInstanceOf( NamedEnumValueConverter.class );
assertThat( convertedBasicType.getValueConverter().getRelationalJavaType().getJavaTypeClass() ).isEqualTo( String.class );
}
}

View File

@ -6,9 +6,6 @@
*/
package org.hibernate.orm.test.envers.integration.customtype;
import java.util.List;
import jakarta.persistence.EntityManager;
import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase;
import org.hibernate.orm.test.envers.Priority;
import org.hibernate.orm.test.envers.entities.customtype.EnumTypeEntity;
@ -17,6 +14,8 @@ import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Test;
import jakarta.persistence.EntityManager;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@ -44,15 +43,13 @@ public class EnumTypeTest extends BaseEnversJPAFunctionalTestCase {
public void testEnumRepresentation() {
EntityManager entityManager = getEntityManager();
entityManager.getTransaction().begin();
List<Object[]> values = entityManager.createNativeQuery(
"SELECT enum1, enum2 FROM EnumTypeEntity_AUD ORDER BY REV ASC"
).getResultList();
final String qry = "SELECT enum1, enum2 FROM EnumTypeEntity_AUD ORDER BY REV ASC";
Object[] results = (Object[]) entityManager.createNativeQuery( qry, "e1_e2" ).getSingleResult();
entityManager.getTransaction().commit();
entityManager.close();
Assert.assertNotNull( values );
Assert.assertEquals( 1, values.size() );
Object[] results = values.get( 0 );
Assert.assertNotNull( results );
Assert.assertEquals( 2, results.length );
Assert.assertEquals( "X", results[0] );
// Compare the Strings to account for, as an example, Oracle

View File

@ -40,4 +40,4 @@ logger.jdbc-extract.name=org.hibernate.orm.jdbc.extract
logger.jdbc-extract.level=trace
logger.additional-jaxb-mapping-producer-impl.name=org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl
logger.additional-jaxb-mapping-producer-impl.level=trace
logger.additional-jaxb-mapping-producer-impl.level=trace

View File

@ -63,6 +63,7 @@ public class EntityOfBasics {
private Date theTimestamp;
private Instant theInstant;
private Gender gender;
private Gender singleCharGender;
private Gender convertedGender;
private Gender ordinalGender;
private Duration theDuration;
@ -148,6 +149,16 @@ public class EntityOfBasics {
this.gender = gender;
}
@Enumerated( EnumType.STRING )
@Column( length = 1 )
public Gender getSingleCharGender() {
return singleCharGender;
}
public void setSingleCharGender(Gender singleCharGender) {
this.singleCharGender = singleCharGender;
}
@Convert( converter = GenderConverter.class )
@Column(name = "converted_gender", length = 1)
@JdbcTypeCode( Types.CHAR )