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.CustomType;
import org.hibernate.type.SerializableType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.BasicJavaType;
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.spi.TypeConfiguration;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TINYINT;
/**
* BasicValue.Resolution resolver for cases where no explicit
* type info was supplied.
@ -344,19 +346,19 @@ public class InferredBasicValueResolver {
TypeConfiguration typeConfiguration) {
return ordinalResolution(
enumJavaType,
integerJavaType( explicitJavaType, typeConfiguration ),
integerJdbcType( explicitJdbcType, typeConfiguration ),
ordinalJavaType( explicitJavaType, typeConfiguration ),
ordinalJdbcType( explicitJdbcType, enumJavaType, typeConfiguration ),
typeConfiguration
);
}
private static JdbcType integerJdbcType(JdbcType explicitJdbcType, TypeConfiguration typeConfiguration) {
private static JdbcType ordinalJdbcType(JdbcType explicitJdbcType, EnumJavaType<?> enumJavaType, TypeConfiguration typeConfiguration) {
return explicitJdbcType != null
? 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 ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
throw new MappingException(

View File

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

View File

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

View File

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

View File

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

View File

@ -104,7 +104,7 @@ public class SmokeTests {
assertThat(
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(
basicType.getJdbcType(),
is( jdbcTypeRegistry.getDescriptor( Types.SMALLINT ) )
is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) )
);

View File

@ -7,7 +7,10 @@
package org.hibernate.orm.test.type;
import org.hibernate.dialect.AbstractHANADialect;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.SybaseASEDialect;
@ -24,8 +27,6 @@ import jakarta.persistence.Query;
import jakarta.persistence.Table;
import jakarta.persistence.TypedQuery;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
@ -113,6 +114,9 @@ public class EnumArrayTest extends BaseNonConfigCoreFunctionalTestCase {
@Test
@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 = MySQLDialect.class )
@SkipForDialect( value = DerbyDialect.class )
@SkipForDialect( value = DB2Dialect.class )
public void testNativeQuery() {
inSession( em -> {
final String op = em.getJdbcServices().getDialect().supportsDistinctFromPredicate() ? "IS NOT DISTINCT FROM" : "=";