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' id 'org.hibernate.build.xjc-jakarta'
} }
repositories {
gradlePluginPortal()
}
description = 'Hibernate\'s core ORM functionality' description = 'Hibernate\'s core ORM functionality'
apply from: rootProject.file( 'gradle/published-java-module.gradle' ) apply from: rootProject.file( 'gradle/published-java-module.gradle' )

View File

@ -6,66 +6,104 @@
*/ */
package org.hibernate.boot.model.process.internal; 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.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.descriptor.converter.spi.EnumValueConverter;
import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan; 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.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 * @author Steve Ebersole
*/ */
public class EnumeratedValueResolution<E extends Enum<E>> implements BasicValue.Resolution<E> { public class EnumeratedValueResolution<E extends Enum<E>,R> implements BasicValue.Resolution<E> {
private final CustomType<Object> enumTypeMapping; public static final String PREFIX = "enum::";
private final JavaType<E> domainJtd;
private final JavaType<?> jdbcJtd; private final EnumValueConverter<E,R> valueConverter;
private final JdbcType jdbcType; private final ConvertedBasicType<E> jdbcMapping;
private final EnumValueConverter<E,?> valueConverter;
public EnumeratedValueResolution( public EnumeratedValueResolution(
CustomType<Object> enumTypeMapping,
JavaType<E> domainJtd,
JavaType<?> jdbcJtd,
JdbcType jdbcType, JdbcType jdbcType,
EnumValueConverter<E, ?> valueConverter) { EnumValueConverter<E, R> valueConverter,
this.enumTypeMapping = enumTypeMapping; MetadataBuildingContext context) {
this.domainJtd = domainJtd;
this.jdbcJtd = jdbcJtd;
this.jdbcType = jdbcType;
this.valueConverter = valueConverter; 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 @Override
public JdbcMapping getJdbcMapping() { public ConvertedBasicType<E> getJdbcMapping() {
return enumTypeMapping; return jdbcMapping;
} }
@Override @Override
public BasicType getLegacyResolvedBasicType() { public ConvertedBasicType<E> getLegacyResolvedBasicType() {
return enumTypeMapping; return jdbcMapping;
} }
@Override @Override
public JavaType<E> getDomainJavaType() { public JavaType<E> getDomainJavaType() {
return domainJtd; return jdbcMapping.getJavaTypeDescriptor();
} }
@Override @Override
public JavaType<?> getRelationalJavaType() { public JavaType<?> getRelationalJavaType() {
return jdbcJtd; return jdbcMapping.getJdbcJavaType();
} }
@Override @Override
public JdbcType getJdbcType() { public JdbcType getJdbcType() {
return jdbcType; return jdbcMapping.getJdbcType();
} }
@Override @Override
public EnumValueConverter getValueConverter() { public EnumValueConverter<E,R> getValueConverter() {
return valueConverter; return valueConverter;
} }
@ -73,4 +111,60 @@ public class EnumeratedValueResolution<E extends Enum<E>> implements BasicValue.
public MutabilityPlan<E> getMutabilityPlan() { public MutabilityPlan<E> getMutabilityPlan() {
return ImmutableMutabilityPlan.instance(); 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 java.util.function.Supplier;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column; 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.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.AdjustableBasicType; import org.hibernate.type.AdjustableBasicType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.SerializableType; import org.hibernate.type.SerializableType;
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter; import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter; import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.java.BasicPluralJavaType; import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.EnumJavaType; 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.JavaType;
import org.hibernate.type.descriptor.java.JavaTypeHelper; import org.hibernate.type.descriptor.java.JavaTypeHelper;
import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.java.MutabilityPlan;
@ -61,8 +60,10 @@ public class InferredBasicValueResolver {
Selectable selectable, Selectable selectable,
String ownerName, String ownerName,
String propertyName, String propertyName,
Dialect dialect, MetadataBuildingContext buildingContext) {
TypeConfiguration typeConfiguration) { final Dialect dialect = buildingContext.getMetadataCollector().getDatabase().getDialect();
final TypeConfiguration typeConfiguration = buildingContext.getBootstrapContext().getTypeConfiguration();
final JavaType<T> reflectedJtd = reflectedJtdResolver.get(); final JavaType<T> reflectedJtd = reflectedJtdResolver.get();
// NOTE : the distinction that is made below wrt `explicitJavaType` and `reflectedJtd` is // NOTE : the distinction that is made below wrt `explicitJavaType` and `reflectedJtd` is
@ -79,7 +80,7 @@ public class InferredBasicValueResolver {
null, null,
explicitJdbcType, explicitJdbcType,
stdIndicators, stdIndicators,
typeConfiguration buildingContext
); );
} }
else if ( JavaTypeHelper.isTemporal( explicitJavaType ) ) { else if ( JavaTypeHelper.isTemporal( explicitJavaType ) ) {
@ -131,7 +132,7 @@ public class InferredBasicValueResolver {
null, null,
explicitJdbcType, explicitJdbcType,
stdIndicators, stdIndicators,
typeConfiguration buildingContext
); );
} }
else if ( JavaTypeHelper.isTemporal( reflectedJtd ) ) { else if ( JavaTypeHelper.isTemporal( reflectedJtd ) ) {
@ -162,14 +163,14 @@ public class InferredBasicValueResolver {
final JavaType<?> elementJtd = containerJtd.getElementJavaType(); final JavaType<?> elementJtd = containerJtd.getElementJavaType();
final BasicType registeredElementType; final BasicType registeredElementType;
if ( elementJtd instanceof EnumJavaType ) { if ( elementJtd instanceof EnumJavaType ) {
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromEnum( final EnumeratedValueResolution<?,?> resolution = fromEnum(
(EnumJavaType) elementJtd, (EnumJavaType<?>) elementJtd,
null, null,
null, null,
stdIndicators, stdIndicators,
typeConfiguration buildingContext
); );
registeredElementType = resolution.getLegacyResolvedBasicType(); registeredElementType = resolution.getJdbcMapping();
} }
else if ( JavaTypeHelper.isTemporal( elementJtd ) ) { else if ( JavaTypeHelper.isTemporal( elementJtd ) ) {
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromTemporal( 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, EnumJavaType<E> enumJavaType,
BasicJavaType<N> explicitJavaType, BasicJavaType<R> explicitJavaType,
JdbcType explicitJdbcType, JdbcType explicitJdbcType,
JdbcTypeIndicators stdIndicators, JdbcTypeIndicators stdIndicators,
TypeConfiguration typeConfiguration) { MetadataBuildingContext context) {
final EnumType enumStyle = stdIndicators.getEnumeratedType() != null final EnumType enumStyle = stdIndicators.getEnumeratedType();
? stdIndicators.getEnumeratedType()
: EnumType.ORDINAL;
switch ( enumStyle ) { if ( enumStyle == EnumType.STRING ) {
case STRING: { //noinspection unchecked
return stringEnumValueResolution( return (EnumeratedValueResolution<E, R>) stringEnumValueResolution(
enumJavaType, enumJavaType,
explicitJavaType, explicitJavaType,
explicitJdbcType, explicitJdbcType,
stdIndicators, stdIndicators,
typeConfiguration context
); );
} }
case ORDINAL: {
return ordinalEnumValueResolution( if ( enumStyle == EnumType.ORDINAL ) {
//noinspection unchecked
return (EnumeratedValueResolution<E, R>) ordinalEnumValueResolution(
enumJavaType, enumJavaType,
explicitJavaType, (BasicJavaType<? extends Number>)explicitJavaType,
explicitJdbcType, explicitJdbcType,
typeConfiguration context
); );
} }
default: {
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 ); throw new MappingException( "Unknown enumeration-style (JPA EnumType) : " + enumStyle );
} }
}
}
private static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E,N> ordinalEnumValueResolution( private static <E extends Enum<E>, N extends Number> EnumeratedValueResolution<E,N> ordinalEnumValueResolution(
EnumJavaType<E> enumJavaType, EnumJavaType<E> enumJavaType,
JavaType<N> explicitJavaType, BasicJavaType<N> explicitJavaType,
JdbcType explicitJdbcType, JdbcType explicitJdbcType,
TypeConfiguration typeConfiguration) { MetadataBuildingContext context) {
return ordinalResolution( final JavaType<N> relationalJavaType = ordinalJavaType( explicitJavaType, context );
enumJavaType, final JdbcType jdbcType = ordinalJdbcType( explicitJdbcType, enumJavaType, context );
ordinalJavaType( explicitJavaType, typeConfiguration ),
ordinalJdbcType( explicitJdbcType, enumJavaType, typeConfiguration ), return new EnumeratedValueResolution<>(
typeConfiguration 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 return explicitJdbcType != null
? explicitJdbcType ? 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 ( explicitJavaType != null ) {
if ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) { if ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
throw new MappingException( throw new MappingException(
@ -386,73 +407,25 @@ public class InferredBasicValueResolver {
return explicitJavaType; return explicitJavaType;
} }
else { 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( private static <E extends Enum<E>> EnumeratedValueResolution<E,String> stringEnumValueResolution(
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(
EnumJavaType<E> enumJavaType, EnumJavaType<E> enumJavaType,
BasicJavaType<?> explicitJavaType, BasicJavaType<?> explicitJavaType,
JdbcType explicitJdbcType, JdbcType explicitJdbcType,
JdbcTypeIndicators stdIndicators, JdbcTypeIndicators stdIndicators,
TypeConfiguration typeConfiguration) { MetadataBuildingContext context) {
final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, typeConfiguration ); final JdbcType jdbcType = explicitJdbcType == null
return stringResolution( ? enumJavaType.getRecommendedJdbcType( stdIndicators )
enumJavaType, : explicitJdbcType;
relationalJtd, final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, context );
stringJdbcType( explicitJdbcType, stdIndicators, relationalJtd ),
typeConfiguration
);
}
private static <E extends Enum<E>> InferredBasicValueResolution<E, String> stringResolution( return new EnumeratedValueResolution<>(
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, jdbcType,
relationalJtd new NamedEnumValueConverter<>( enumJavaType, jdbcType, relationalJtd ),
), context
typeConfiguration
),
typeConfiguration
);
return new InferredBasicValueResolution<>(
customType,
enumJavaType,
relationalJtd,
jdbcType,
customType,
ImmutableMutabilityPlan.instance()
); );
} }
@ -462,7 +435,10 @@ public class InferredBasicValueResolver {
: relationalJtd.getRecommendedJdbcType( stdIndicators ); : 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 ( explicitJavaType != null ) {
if ( ! String.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) { if ( ! String.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
throw new MappingException( throw new MappingException(
@ -474,7 +450,7 @@ public class InferredBasicValueResolver {
return (JavaType<String>) explicitJavaType; return (JavaType<String>) explicitJavaType;
} }
else { else {
return typeConfiguration.getJavaTypeRegistry() return context.getMetadataCollector().getTypeConfiguration().getJavaTypeRegistry()
.getDescriptor( stdIndicators.getColumnLength() == 1 ? Character.class : String.class ); .getDescriptor( stdIndicators.getColumnLength() == 1 ? Character.class : String.class );
} }
} }

View File

@ -112,7 +112,10 @@ public class SqlStatementLogger {
*/ */
@AllowSysOut @AllowSysOut
public void logStatement(String statement, Formatter formatter) { public void logStatement(String statement, Formatter formatter) {
if ( logToStdout || LOG.isDebugEnabled() ) { if ( !logToStdout && !LOG.isDebugEnabled() ) {
return;
}
try { try {
if ( format ) { if ( format ) {
statement = formatter.format( statement ); statement = formatter.format( statement );
@ -124,7 +127,7 @@ public class SqlStatementLogger {
catch (RuntimeException ex) { catch (RuntimeException ex) {
LOG.warn( "Couldn't format statement", ex ); LOG.warn( "Couldn't format statement", ex );
} }
}
LOG.debug( statement ); LOG.debug( statement );
if ( logToStdout ) { if ( logToStdout ) {
String prefix = highlight ? "\u001b[35m[Hibernate]\u001b[0m " : "Hibernate: "; 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.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor; import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext; 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.InferredBasicValueResolution;
import org.hibernate.boot.model.process.internal.InferredBasicValueResolver; import org.hibernate.boot.model.process.internal.InferredBasicValueResolver;
import org.hibernate.boot.model.process.internal.NamedBasicTypeResolution; import org.hibernate.boot.model.process.internal.NamedBasicTypeResolution;
@ -485,8 +486,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
column, column,
ownerName, ownerName,
propertyName, propertyName,
getDialect(), getBuildingContext()
getTypeConfiguration()
); );
} }
} }
@ -569,7 +569,6 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
} }
}; };
// Name could refer to: // Name could refer to:
// 1) a named converter - HBM support for JPA's AttributeConverter via its `type="..."` XML attribute // 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 // 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 ) ) { if ( name.startsWith( BasicTypeImpl.EXTERNALIZED_PREFIX ) ) {
final BasicTypeImpl<Object> basicType = context.getBootstrapContext().resolveAdHocBasicType( name ); final BasicTypeImpl<Object> basicType = context.getBootstrapContext().resolveAdHocBasicType( name );

View File

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

View File

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

View File

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

View File

@ -14,6 +14,11 @@ import java.sql.SQLException;
* Contract for extracting values from a JDBC {@link ResultSet} or * Contract for extracting values from a JDBC {@link ResultSet} or
* from output the parameters of a {@link CallableStatement}. * 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 * @author Steve Ebersole
*/ */
public interface ValueExtractor<X> { 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 JdbcTypeRegistry registry = context.getTypeConfiguration().getJdbcTypeRegistry();
final EnumType type = context.getEnumeratedType(); final EnumType type = context.getEnumeratedType();
switch ( type == null ? ORDINAL : type ) { switch ( type == null ? ORDINAL : type ) {
case ORDINAL: case ORDINAL: {
return registry.getDescriptor( hasManyValues() ? SMALLINT : TINYINT ); return registry.getDescriptor( hasManyValues() ? SMALLINT : TINYINT );
case STRING: }
case STRING: {
if ( context.getColumnLength() == 1 ) { if ( context.getColumnLength() == 1 ) {
return context.isNationalized() return context.isNationalized()
? registry.getDescriptor( NCHAR ) ? registry.getDescriptor( NCHAR )
@ -48,10 +49,12 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
return context.isNationalized() return context.isNationalized()
? registry.getDescriptor( NVARCHAR ) ? registry.getDescriptor( NVARCHAR )
: registry.getDescriptor( VARCHAR ); : registry.getDescriptor( VARCHAR );
default: }
default: {
throw new AssertionFailure("unknown EnumType"); throw new AssertionFailure("unknown EnumType");
} }
} }
}
public boolean hasManyValues() { public boolean hasManyValues() {
// a bit arbitrary, but gives us some headroom // a bit arbitrary, but gives us some headroom

View File

@ -10,6 +10,7 @@ import java.io.Serializable;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.type.descriptor.java.ArrayJavaType; import org.hibernate.type.descriptor.java.ArrayJavaType;
@ -70,35 +71,12 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// descriptor access // descriptor access
public void forEachDescriptor(Consumer<JavaType<?>> consumer) {
descriptorsByType.values().forEach( consumer );
}
public <T> JavaType<T> getDescriptor(Type javaType) { public <T> JavaType<T> getDescriptor(Type javaType) {
return resolveDescriptor( 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) { public void addDescriptor(JavaType<?> descriptor) {
@ -115,6 +93,7 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
} }
public <J> JavaType<J> findDescriptor(Type javaType) { public <J> JavaType<J> findDescriptor(Type javaType) {
//noinspection unchecked
return (JavaType<J>) descriptorsByType.get( javaType ); return (JavaType<J>) descriptorsByType.get( javaType );
} }
@ -200,25 +179,19 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
final ParameterizedType parameterizedType = (ParameterizedType) javaType; final ParameterizedType parameterizedType = (ParameterizedType) javaType;
javaTypeClass = (Class<J>) parameterizedType.getRawType(); javaTypeClass = (Class<J>) parameterizedType.getRawType();
} }
final MutabilityPlan<J> mutabilityPlan;
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan(
javaType, javaType,
typeConfiguration typeConfiguration
); );
if ( determinedPlan != null ) { final MutabilityPlan<J> mutabilityPlan = (determinedPlan != null)
mutabilityPlan = determinedPlan; ? determinedPlan
} : (MutabilityPlan<J>) MutableMutabilityPlan.INSTANCE;
else {
mutabilityPlan = (MutabilityPlan<J>) MutableMutabilityPlan.INSTANCE; return entity
} ? new EntityJavaType<>( javaTypeClass, mutabilityPlan )
return entity ? new EntityJavaType<>( javaTypeClass, mutabilityPlan )
: new JavaTypeBasicAdaptor<>( 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.Property;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.type.CustomType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -56,8 +55,7 @@ public class NestedEmbeddableMetadataTest {
assertEquals( (Long) 500L, selectable.getLength() ); assertEquals( (Long) 500L, selectable.getLength() );
Component amountMetadata = (Component) investmentMetadata.getProperty( "amount" ).getValue(); Component amountMetadata = (Component) investmentMetadata.getProperty( "amount" ).getValue();
SimpleValue currencyMetadata = (SimpleValue) amountMetadata.getProperty( "currency" ).getValue(); SimpleValue currencyMetadata = (SimpleValue) amountMetadata.getProperty( "currency" ).getValue();
CustomType<Object> currencyType = (CustomType<Object>) currencyMetadata.getType(); int[] currencySqlTypes = currencyMetadata.getType().getSqlTypeCodes( metadata );
int[] currencySqlTypes = currencyType.getSqlTypeCodes( metadata );
assertEquals( 1, currencySqlTypes.length ); assertEquals( 1, currencySqlTypes.length );
assertJdbcTypeCode( assertJdbcTypeCode(
typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARCHAR ).getJdbcTypeCode(), 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.Property;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value; 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.type.spi.TypeConfiguration;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hibernate.testing.junit4.ExtraAssertions.assertJdbcTypeCode; import static org.hibernate.testing.junit4.ExtraAssertions.assertJdbcTypeCode;
@ -54,8 +51,7 @@ public class FieldAccessedNestedEmbeddableMetadataTest {
assertEquals( (Long) 500L, selectable.getLength() ); assertEquals( (Long) 500L, selectable.getLength() );
Component amountMetadata = (Component) investmentMetadata.getProperty( "amount" ).getValue(); Component amountMetadata = (Component) investmentMetadata.getProperty( "amount" ).getValue();
SimpleValue currencyMetadata = (SimpleValue) amountMetadata.getProperty( "currency" ).getValue(); SimpleValue currencyMetadata = (SimpleValue) amountMetadata.getProperty( "currency" ).getValue();
CustomType<Object> currencyType = (CustomType<Object>) currencyMetadata.getType(); int[] currencySqlTypes = currencyMetadata.getType().getSqlTypeCodes( metadata );
int[] currencySqlTypes = currencyType.getSqlTypeCodes( metadata );
assertEquals( 1, currencySqlTypes.length ); assertEquals( 1, currencySqlTypes.length );
assertJdbcTypeCode( assertJdbcTypeCode(
typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARCHAR ).getJdbcTypeCode(), typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.VARCHAR ).getJdbcTypeCode(),

View File

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

View File

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

View File

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

View File

@ -8,12 +8,6 @@ package org.hibernate.orm.test.bootstrap.binding.annotations.basics;
import java.sql.Types; import java.sql.Types;
import java.util.function.Consumer; 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.annotations.JdbcTypeCode;
import org.hibernate.mapping.BasicValue; 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.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter; 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.descriptor.jdbc.JdbcType;
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.usertype.UserType;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope; import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.junit.jupiter.api.Test; 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.ORDINAL;
import static jakarta.persistence.EnumType.STRING; import static jakarta.persistence.EnumType.STRING;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
@ -128,9 +126,6 @@ public class EnumResolutionTests {
//noinspection rawtypes //noinspection rawtypes
final Class converterType = ( (JpaAttributeConverter) converter ).getConverterBean().getBeanClass(); final Class converterType = ( (JpaAttributeConverter) converter ).getConverterBean().getBeanClass();
assertThat( converterType, equalTo( ConverterImpl.class ) ); 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") @SuppressWarnings("rawtypes")
private void verifyEnumResolution( private void verifyEnumResolution(
Property property, Property property,
@ -164,13 +175,6 @@ public class EnumResolutionTests {
valueConverter -> { valueConverter -> {
assertThat( valueConverter, notNullValue() ); assertThat( valueConverter, notNullValue() );
assertThat( valueConverter, instanceOf( converterClass ) ); 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, Property property,
int jdbcCode, int jdbcCode,
Class<?> javaType, Class<?> javaType,
Consumer<BasicValueConverter> converterChecker, Consumer<BasicValueConverter> converterChecker) {
Consumer<BasicType> legacyTypeChecker) {
final BasicValue.Resolution<?> resolution = ( (BasicValue) property.getValue() ).resolve(); final BasicValue.Resolution<?> resolution = ( (BasicValue) property.getValue() ).resolve();
final TypeConfiguration typeConfiguration = ( (BasicValue) property.getValue() ).getTypeConfiguration(); final TypeConfiguration typeConfiguration = ( (BasicValue) property.getValue() ).getTypeConfiguration();
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( jdbcCode ); final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( jdbcCode );
// verify the interpretations used for reading // verify the interpretations used for reading
assertThat( resolution.getJdbcType(), is( jdbcType ) ); assertThat( resolution.getJdbcType(), is( jdbcType ) );
assertThat( resolution.getRelationalJavaType().getJavaTypeClass(), equalTo( javaType ) ); assertThat( resolution.getRelationalJavaType().getJavaTypeClass(), equalTo( javaType ) );
@ -195,9 +199,6 @@ public class EnumResolutionTests {
assertThat( jdbcMapping.getJdbcJavaType(), equalTo( resolution.getRelationalJavaType() ) ); assertThat( jdbcMapping.getJdbcJavaType(), equalTo( resolution.getRelationalJavaType() ) );
converterChecker.accept( resolution.getValueConverter() ); converterChecker.accept( resolution.getValueConverter() );
// verify the (legacy) interpretations used for writing
legacyTypeChecker.accept( resolution.getLegacyResolvedBasicType() );
} }
@Entity( name = "EntityWithEnums" ) @Entity( name = "EntityWithEnums" )
@ -224,6 +225,10 @@ public class EnumResolutionTests {
@Enumerated( ORDINAL ) @Enumerated( ORDINAL )
@JdbcTypeCode( Types.SMALLINT ) @JdbcTypeCode( Types.SMALLINT )
private Values explicitEnum; private Values explicitEnum;
@Enumerated( STRING )
@Column( length = 1 )
private Values singleCharEnum;
} }
enum Values { FIRST, SECOND } enum Values { FIRST, SECOND }

View File

@ -6,8 +6,11 @@
*/ */
package org.hibernate.orm.test.bootstrap.spi.delegation; package org.hibernate.orm.test.bootstrap.spi.delegation;
import java.util.Set;
import org.hibernate.boot.spi.AbstractDelegatingMetadata; import org.hibernate.boot.spi.AbstractDelegatingMetadata;
import org.hibernate.boot.spi.MetadataImplementor; 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 * 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.BasicAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; 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.NamedEnumValueConverter;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter; import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter; import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter; import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter;
import org.hibernate.persister.entity.EntityPersister; 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.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
@ -92,9 +91,8 @@ public class SmokeTests {
assertThat( genderAttrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) ); assertThat( genderAttrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) );
final CustomType<?> basicType = (CustomType<?>) genderAttrMapping.getJdbcMapping(); final ConvertedBasicType<?> jdbcMapping = (ConvertedBasicType<?>) genderAttrMapping.getJdbcMapping();
final org.hibernate.type.EnumType<?> enumType = (org.hibernate.type.EnumType<?>) basicType.getUserType(); final EnumValueConverter<?, ?> valueConverter = (EnumValueConverter<?, ?>) jdbcMapping.getValueConverter();
final EnumValueConverter<?, ?> valueConverter = enumType.getEnumValueConverter();
assertThat( valueConverter, instanceOf( OrdinalEnumValueConverter.class ) ); assertThat( valueConverter, instanceOf( OrdinalEnumValueConverter.class ) );
assertThat( assertThat(
valueConverter.getDomainJavaType().getJavaTypeClass(), valueConverter.getDomainJavaType().getJavaTypeClass(),
@ -117,9 +115,8 @@ public class SmokeTests {
assertThat( attrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) ); assertThat( attrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) );
final CustomType<?> basicType = (CustomType<?>) attrMapping.getJdbcMapping(); final ConvertedBasicType<?> jdbcMapping = (ConvertedBasicType<?>) attrMapping.getJdbcMapping();
final org.hibernate.type.EnumType<?> enumType = (org.hibernate.type.EnumType<?>) basicType.getUserType(); final EnumValueConverter<?, ?> valueConverter = (EnumValueConverter<?, ?>) jdbcMapping.getValueConverter();
final EnumValueConverter<?, ?> valueConverter = enumType.getEnumValueConverter();
assertThat( valueConverter, instanceOf( NamedEnumValueConverter.class ) ); assertThat( valueConverter, instanceOf( NamedEnumValueConverter.class ) );
assertThat( assertThat(
valueConverter.getDomainJavaType().getJavaTypeClass(), valueConverter.getDomainJavaType().getJavaTypeClass(),
@ -142,7 +139,7 @@ public class SmokeTests {
assertThat( attrMapping.getJavaType().getJavaTypeClass(), equalTo( Gender.class ) ); 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, instanceOf( JpaAttributeConverter.class ) );
assertThat( valueConverter.getDomainJavaType(), is( attrMapping.getJavaType() ) ); assertThat( valueConverter.getDomainJavaType(), is( attrMapping.getJavaType() ) );
assertThat( valueConverter.getRelationalJavaType().getJavaTypeClass(), equalTo( Character.class ) ); 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 java.sql.Types;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter; import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; 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.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.CustomType; import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType; import org.hibernate.type.EnumType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.testing.hamcrest.AssignableMatcher; import org.hibernate.testing.hamcrest.AssignableMatcher;
@ -117,9 +119,6 @@ public class SmokeTests {
public void testConvertedHqlInterpretation(SessionFactoryScope scope) { public void testConvertedHqlInterpretation(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
final JdbcTypeRegistry jdbcTypeRegistry = session.getFactory()
.getTypeConfiguration()
.getJdbcTypeRegistry();
final QueryImplementor<Gender> query = session.createQuery( "select e.gender from SimpleEntity e", Gender.class ); final QueryImplementor<Gender> query = session.createQuery( "select e.gender from SimpleEntity e", Gender.class );
final SqmQueryImplementor<Gender> hqlQuery = (SqmQueryImplementor<Gender>) query; final SqmQueryImplementor<Gender> hqlQuery = (SqmQueryImplementor<Gender>) query;
final SqmSelectStatement<Gender> sqmStatement = (SqmSelectStatement<Gender>) hqlQuery.getSqmStatement(); final SqmSelectStatement<Gender> sqmStatement = (SqmSelectStatement<Gender>) hqlQuery.getSqmStatement();
@ -167,17 +166,11 @@ public class SmokeTests {
final ColumnReference columnReference = (ColumnReference) selectedExpression; final ColumnReference columnReference = (ColumnReference) selectedExpression;
assertThat( columnReference.getExpressionText(), is( "s1_0.gender" ) ); assertThat( columnReference.getExpressionText(), is( "s1_0.gender" ) );
final JdbcMappingContainer selectedExpressible = selectedExpression.getExpressionType(); final JdbcMapping selectedExpressible = selectedExpression.getExpressionType().getSingleJdbcMapping();
assertThat( selectedExpressible, instanceOf( CustomType.class ) ); assertThat( selectedExpressible.getJdbcType().isInteger(), is( true ) );
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 EnumValueConverter<?, ?> enumConverter = (EnumValueConverter<?, ?>) selectedExpressible.getValueConverter();
assertThat( enumConverter.getRelationalJavaType().getJavaTypeClass(), AssignableMatcher.assignableTo( Integer.class ) );
assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) ); assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) );
final DomainResult<?> domainResult = sqlAst.getDomainResultDescriptors().get( 0 ); final DomainResult<?> domainResult = sqlAst.getDomainResultDescriptors().get( 0 );

View File

@ -41,6 +41,7 @@ import static org.junit.Assert.assertThat;
* @author Christian Beikov * @author Christian Beikov
*/ */
@SkipForDialect(value = SybaseASEDialect.class, comment = "Sybase or the driver are trimming trailing zeros in byte arrays") @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 { public class EnumArrayTest extends BaseNonConfigCoreFunctionalTestCase {
@Override @Override

View File

@ -16,9 +16,13 @@ import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.ConvertedBasicType;
import org.hibernate.type.CustomType; import org.hibernate.type.CustomType;
import org.hibernate.type.EnumType; import org.hibernate.type.EnumType;
import org.hibernate.type.Type; 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). * 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 ) ); typeDefinition.setParameter( EnumType.NAMED, parameters.getProperty( EnumType.NAMED ) );
} }
else { 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) { private boolean isEnumType(Type type, String typeName) {
// Check if a custom type implementation is used and it extends the EnumType directly. if ( type instanceof ConvertedBasicType ) {
if ( type instanceof CustomType ) { final ConvertedBasicType<?> convertedType = (ConvertedBasicType<?>) type;
final CustomType<?> customType = (CustomType<?>) type; return convertedType.getValueConverter() instanceof EnumValueConverter;
if ( customType.getUserType() instanceof EnumType ) { }
return true; return false;
}
}
// Check if it is an EnumType without a custom type
return EnumType.class.getName().equals( typeName );
} }
} }

View File

@ -6,11 +6,13 @@
*/ */
package org.hibernate.orm.test.envers.entities.customtype; package org.hibernate.orm.test.envers.entities.customtype;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.EnumType; import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated; import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.SqlResultSetMapping;
import org.hibernate.envers.Audited; import org.hibernate.envers.Audited;
@ -19,6 +21,13 @@ import org.hibernate.envers.Audited;
*/ */
@Entity @Entity
@Audited @Audited
@SqlResultSetMapping(
name = "e1_e2",
columns = {
@ColumnResult( name = "enum1", type = String.class ),
@ColumnResult( name = "enum2", type = Integer.class )
}
)
public class EnumTypeEntity { public class EnumTypeEntity {
public static enum E1 {X, Y} 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.Arrays;
import java.util.List; import java.util.List;
import jakarta.persistence.EntityManager; 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.BaseEnversJPAFunctionalTestCase;
import org.hibernate.orm.test.envers.Priority; 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;
import org.hibernate.orm.test.envers.entities.collection.EnumSetEntity.E1; 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.entities.collection.EnumSetEntity.E2;
import org.hibernate.orm.test.envers.tools.TestTools; 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.hibernate.testing.TestForIssue;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/** /**
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
*/ */
@ -98,19 +115,48 @@ public class EnumSet extends BaseEnversJPAFunctionalTestCase {
@TestForIssue(jiraKey = "HHH-7780") @TestForIssue(jiraKey = "HHH-7780")
public void testEnumRepresentation() { public void testEnumRepresentation() {
EntityManager entityManager = getEntityManager(); EntityManager entityManager = getEntityManager();
List<Object> enums1 = entityManager.createNativeQuery(
"SELECT enums1 FROM EnumSetEntity_enums1_AUD ORDER BY REV ASC" verifyModel( entityManager );
).getResultList();
List<Object> enums2 = entityManager.createNativeQuery( {
"SELECT enums2 FROM EnumSetEntity_enums2_AUD ORDER BY REV ASC" final String qry = "SELECT enums1 FROM EnumSetEntity_enums1_AUD ORDER BY REV ASC";
).getResultList(); 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(); 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 ); final EntityMappingType entityMapping = mappingMetamodel.getEntityDescriptor( EnumSetEntity.class );
// Compare the Strings to account for, as an example, Oracle returning a BigDecimal instead of an int. final PluralAttributeMapping attributeMapping = (PluralAttributeMapping) entityMapping.findDeclaredAttributeMapping( "enums1" );
Assert.assertEquals( "0", enum2.toString() ); 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; 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.BaseEnversJPAFunctionalTestCase;
import org.hibernate.orm.test.envers.Priority; import org.hibernate.orm.test.envers.Priority;
import org.hibernate.orm.test.envers.entities.customtype.EnumTypeEntity; 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.Assert;
import org.junit.Test; import org.junit.Test;
import jakarta.persistence.EntityManager;
/** /**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/ */
@ -44,15 +43,13 @@ public class EnumTypeTest extends BaseEnversJPAFunctionalTestCase {
public void testEnumRepresentation() { public void testEnumRepresentation() {
EntityManager entityManager = getEntityManager(); EntityManager entityManager = getEntityManager();
entityManager.getTransaction().begin(); entityManager.getTransaction().begin();
List<Object[]> values = entityManager.createNativeQuery(
"SELECT enum1, enum2 FROM EnumTypeEntity_AUD ORDER BY REV ASC" final String qry = "SELECT enum1, enum2 FROM EnumTypeEntity_AUD ORDER BY REV ASC";
).getResultList(); Object[] results = (Object[]) entityManager.createNativeQuery( qry, "e1_e2" ).getSingleResult();
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
Assert.assertNotNull( values ); Assert.assertNotNull( results );
Assert.assertEquals( 1, values.size() );
Object[] results = values.get( 0 );
Assert.assertEquals( 2, results.length ); Assert.assertEquals( 2, results.length );
Assert.assertEquals( "X", results[0] ); Assert.assertEquals( "X", results[0] );
// Compare the Strings to account for, as an example, Oracle // Compare the Strings to account for, as an example, Oracle

View File

@ -63,6 +63,7 @@ public class EntityOfBasics {
private Date theTimestamp; private Date theTimestamp;
private Instant theInstant; private Instant theInstant;
private Gender gender; private Gender gender;
private Gender singleCharGender;
private Gender convertedGender; private Gender convertedGender;
private Gender ordinalGender; private Gender ordinalGender;
private Duration theDuration; private Duration theDuration;
@ -148,6 +149,16 @@ public class EntityOfBasics {
this.gender = gender; 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 ) @Convert( converter = GenderConverter.class )
@Column(name = "converted_gender", length = 1) @Column(name = "converted_gender", length = 1)
@JdbcTypeCode( Types.CHAR ) @JdbcTypeCode( Types.CHAR )