HHH-15912 adaptively choose the DDL type for ORDINAL enums based on the number of members

this amounts to a reversion of HHH-15288 for 99.99% of enums
This commit is contained in:
Gavin 2022-12-20 15:30:34 +01:00 committed by Gavin King
parent 7007bafe55
commit 5089df2036
8 changed files with 50 additions and 32 deletions

View File

@ -25,7 +25,6 @@ import org.hibernate.type.AdjustableBasicType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType; import org.hibernate.type.CustomType;
import org.hibernate.type.SerializableType; import org.hibernate.type.SerializableType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.BasicPluralJavaType; import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.java.EnumJavaType; import org.hibernate.type.descriptor.java.EnumJavaType;
@ -38,6 +37,9 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType; import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TINYINT;
/** /**
* BasicValue.Resolution resolver for cases where no explicit * BasicValue.Resolution resolver for cases where no explicit
* type info was supplied. * type info was supplied.
@ -344,19 +346,19 @@ public class InferredBasicValueResolver {
TypeConfiguration typeConfiguration) { TypeConfiguration typeConfiguration) {
return ordinalResolution( return ordinalResolution(
enumJavaType, enumJavaType,
integerJavaType( explicitJavaType, typeConfiguration ), ordinalJavaType( explicitJavaType, typeConfiguration ),
integerJdbcType( explicitJdbcType, typeConfiguration ), ordinalJdbcType( explicitJdbcType, enumJavaType, typeConfiguration ),
typeConfiguration typeConfiguration
); );
} }
private static JdbcType integerJdbcType(JdbcType explicitJdbcType, TypeConfiguration typeConfiguration) { private static JdbcType ordinalJdbcType(JdbcType explicitJdbcType, EnumJavaType<?> enumJavaType, TypeConfiguration typeConfiguration) {
return explicitJdbcType != null return explicitJdbcType != null
? explicitJdbcType ? explicitJdbcType
: typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SMALLINT ); : typeConfiguration.getJdbcTypeRegistry().getDescriptor( enumJavaType.hasManyValues() ? SMALLINT : TINYINT );
} }
private static <N extends Number> JavaType<N> integerJavaType(JavaType<N> explicitJavaType, TypeConfiguration typeConfiguration) { private static <N extends Number> JavaType<N> ordinalJavaType(JavaType<N> explicitJavaType, TypeConfiguration typeConfiguration) {
if ( explicitJavaType != null ) { if ( explicitJavaType != null ) {
if ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) { if ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
throw new MappingException( throw new MappingException(

View File

@ -1326,7 +1326,6 @@ public abstract class Dialect implements ConversionContext {
public boolean equivalentTypes(int typeCode1, int typeCode2) { public boolean equivalentTypes(int typeCode1, int typeCode2) {
return typeCode1==typeCode2 return typeCode1==typeCode2
|| isNumericOrDecimal(typeCode1) && isNumericOrDecimal(typeCode2) || isNumericOrDecimal(typeCode1) && isNumericOrDecimal(typeCode2)
|| isSmallOrTinyInt(typeCode1) && isSmallOrTinyInt(typeCode2) //special case for HHH-15288 migration
// || isIntegral(typeCode1) && isIntegral(typeCode2) // || isIntegral(typeCode1) && isIntegral(typeCode2)
|| isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2) || isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2)
|| isVarcharType(typeCode1) && isVarcharType(typeCode2) || isVarcharType(typeCode1) && isVarcharType(typeCode2)

View File

@ -6,15 +6,22 @@
*/ */
package org.hibernate.type.descriptor.java; package org.hibernate.type.descriptor.java;
import java.sql.Types;
import jakarta.persistence.EnumType; import jakarta.persistence.EnumType;
import org.hibernate.type.SqlTypes; import org.hibernate.AssertionFailure;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
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.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import static jakarta.persistence.EnumType.ORDINAL;
import static org.hibernate.type.SqlTypes.CHAR;
import static org.hibernate.type.SqlTypes.NCHAR;
import static org.hibernate.type.SqlTypes.NVARCHAR;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.VARCHAR;
/** /**
* Describes a Java {@code enum} type. * Describes a Java {@code enum} type.
* *
@ -27,21 +34,27 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
@Override @Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
JdbcTypeRegistry registry = context.getTypeConfiguration().getJdbcTypeRegistry(); final JdbcTypeRegistry registry = context.getTypeConfiguration().getJdbcTypeRegistry();
if ( context.getEnumeratedType() != null && context.getEnumeratedType() == EnumType.STRING ) { final EnumType type = context.getEnumeratedType();
if ( context.getColumnLength() == 1 ) { switch ( type == null ? ORDINAL : type ) {
case ORDINAL:
return registry.getDescriptor( hasManyValues() ? SMALLINT : TINYINT );
case STRING:
if ( context.getColumnLength() == 1 ) {
return context.isNationalized()
? registry.getDescriptor( NCHAR )
: registry.getDescriptor( CHAR );
}
return context.isNationalized() return context.isNationalized()
? registry.getDescriptor( Types.NCHAR ) ? registry.getDescriptor( NVARCHAR )
: registry.getDescriptor( Types.CHAR ); : registry.getDescriptor( VARCHAR );
} default:
throw new AssertionFailure("unknown EnumType");
}
}
return context.isNationalized() public boolean hasManyValues() {
? registry.getDescriptor( Types.NVARCHAR ) return getJavaTypeClass().getEnumConstants().length > 128; // a bit arbitrary, but gives us some headroom
: registry.getDescriptor( Types.VARCHAR );
}
else {
return registry.getDescriptor( SqlTypes.SMALLINT );
}
} }
@Override @Override

View File

@ -79,7 +79,7 @@ public class EnumeratedSmokeTest extends BaseUnitTestCase {
assertThat( hibernateMappingEnumType.isOrdinal(), is(expectedJpaEnumType==EnumType.ORDINAL) ); assertThat( hibernateMappingEnumType.isOrdinal(), is(expectedJpaEnumType==EnumType.ORDINAL) );
final int expectedJdbcTypeCode = jdbcRegistry.getDescriptor( final int expectedJdbcTypeCode = jdbcRegistry.getDescriptor(
expectedJpaEnumType == EnumType.ORDINAL ? expectedJpaEnumType == EnumType.ORDINAL ?
Types.SMALLINT : Types.TINYINT :
Types.VARCHAR Types.VARCHAR
).getJdbcTypeCode(); ).getJdbcTypeCode();
assertThat( assertThat(

View File

@ -60,7 +60,7 @@ public class EnumResolutionTests {
verifyEnumResolution( verifyEnumResolution(
entityBinding.getProperty( "rawEnum" ), entityBinding.getProperty( "rawEnum" ),
Types.SMALLINT, Types.TINYINT,
Integer.class, Integer.class,
OrdinalEnumValueConverter.class, OrdinalEnumValueConverter.class,
true true
@ -75,7 +75,7 @@ public class EnumResolutionTests {
verifyEnumResolution( verifyEnumResolution(
entityBinding.getProperty( "unspecifiedMappingEnum" ), entityBinding.getProperty( "unspecifiedMappingEnum" ),
Types.SMALLINT, Types.TINYINT,
Integer.class, Integer.class,
OrdinalEnumValueConverter.class, OrdinalEnumValueConverter.class,
true true
@ -90,7 +90,7 @@ public class EnumResolutionTests {
verifyEnumResolution( verifyEnumResolution(
entityBinding.getProperty( "ordinalEnum" ), entityBinding.getProperty( "ordinalEnum" ),
Types.SMALLINT, Types.TINYINT,
Integer.class, Integer.class,
OrdinalEnumValueConverter.class, OrdinalEnumValueConverter.class,
true true
@ -143,7 +143,7 @@ public class EnumResolutionTests {
verifyEnumResolution( verifyEnumResolution(
entityBinding.getProperty( "explicitEnum" ), entityBinding.getProperty( "explicitEnum" ),
Types.TINYINT, Types.SMALLINT,
Integer.class, Integer.class,
OrdinalEnumValueConverter.class, OrdinalEnumValueConverter.class,
true true
@ -222,7 +222,7 @@ public class EnumResolutionTests {
private Values namedEnum; private Values namedEnum;
@Enumerated( ORDINAL ) @Enumerated( ORDINAL )
@JdbcTypeCode( Types.TINYINT ) @JdbcTypeCode( Types.SMALLINT )
private Values explicitEnum; private Values explicitEnum;
} }

View File

@ -104,7 +104,7 @@ public class SmokeTests {
assertThat( assertThat(
jdbcTypeRegistry.getDescriptor( valueConverter.getJdbcTypeCode() ), jdbcTypeRegistry.getDescriptor( valueConverter.getJdbcTypeCode() ),
is( jdbcTypeRegistry.getDescriptor( Types.SMALLINT ) ) is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) )
); );
} }

View File

@ -191,7 +191,7 @@ public class SmokeTests {
assertThat( enumConverter.getRelationalJavaType().getJavaTypeClass(), AssignableMatcher.assignableTo( Integer.class ) ); assertThat( enumConverter.getRelationalJavaType().getJavaTypeClass(), AssignableMatcher.assignableTo( Integer.class ) );
assertThat( assertThat(
basicType.getJdbcType(), basicType.getJdbcType(),
is( jdbcTypeRegistry.getDescriptor( Types.SMALLINT ) ) is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) )
); );

View File

@ -7,7 +7,10 @@
package org.hibernate.orm.test.type; package org.hibernate.orm.test.type;
import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.AbstractHANADialect;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.dialect.SybaseASEDialect;
@ -24,8 +27,6 @@ import jakarta.persistence.Query;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.persistence.TypedQuery; import jakarta.persistence.TypedQuery;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test; import org.junit.Test;
@ -113,6 +114,9 @@ public class EnumArrayTest extends BaseNonConfigCoreFunctionalTestCase {
@Test @Test
@SkipForDialect( value = HSQLDialect.class, comment = "HSQL does not like plain parameters in the distinct from predicate") @SkipForDialect( value = HSQLDialect.class, comment = "HSQL does not like plain parameters in the distinct from predicate")
@SkipForDialect( value = OracleDialect.class, comment = "Oracle requires a special function to compare XML") @SkipForDialect( value = OracleDialect.class, comment = "Oracle requires a special function to compare XML")
@SkipForDialect( value = MySQLDialect.class )
@SkipForDialect( value = DerbyDialect.class )
@SkipForDialect( value = DB2Dialect.class )
public void testNativeQuery() { public void testNativeQuery() {
inSession( em -> { inSession( em -> {
final String op = em.getJdbcServices().getDialect().supportsDistinctFromPredicate() ? "IS NOT DISTINCT FROM" : "="; final String op = em.getJdbcServices().getDialect().supportsDistinctFromPredicate() ? "IS NOT DISTINCT FROM" : "=";