Skip to content

HHH-16311 - Migrate away from UserType for enum handling #6246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions hibernate-core/hibernate-core.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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' )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,165 @@
*/
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;
}

@Override
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 );
}
}
Loading