mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-22 11:06:08 +00:00
HHH-16125 attempt to support PostgreSQL enum types
This commit is contained in:
parent
1d7be9512a
commit
fb9c007bdd
@ -77,7 +77,6 @@
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
||||
@ -796,7 +795,7 @@ public boolean supportsColumnCheck() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnumTypeDeclaration(String[] values) {
|
||||
public String getEnumTypeDeclaration(String name, String[] values) {
|
||||
StringBuilder type = new StringBuilder();
|
||||
type.append( "enum (" );
|
||||
String separator = "";
|
||||
|
@ -15,16 +15,20 @@
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.type.ConvertedBasicType;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.internal.NativeEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
|
||||
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.ObjectJavaType;
|
||||
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.NativeEnumJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
@ -32,9 +36,8 @@
|
||||
import jakarta.persistence.EnumType;
|
||||
|
||||
import static org.hibernate.type.SqlTypes.CHAR;
|
||||
import static org.hibernate.type.SqlTypes.SMALLINT;
|
||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.isCharacterType;
|
||||
|
||||
/**
|
||||
* Resolution for {@linkplain Enum enum} mappings using {@link jakarta.persistence.Enumerated},
|
||||
@ -50,8 +53,7 @@ public class EnumeratedValueResolution<E extends Enum<E>,R> implements BasicValu
|
||||
|
||||
public EnumeratedValueResolution(
|
||||
JdbcType jdbcType,
|
||||
EnumValueConverter<E, R> valueConverter,
|
||||
MetadataBuildingContext context) {
|
||||
EnumValueConverter<E, R> valueConverter) {
|
||||
this.valueConverter = valueConverter;
|
||||
|
||||
final String externalizableName = createName( valueConverter );
|
||||
@ -70,7 +72,8 @@ private String createName(EnumValueConverter<E, R> valueConverter) {
|
||||
}
|
||||
|
||||
private static EnumType enumStyle(EnumValueConverter<?,?> valueConverter) {
|
||||
if ( valueConverter instanceof NamedEnumValueConverter ) {
|
||||
if ( valueConverter instanceof NamedEnumValueConverter
|
||||
|| valueConverter instanceof NativeEnumValueConverter ) {
|
||||
return EnumType.STRING;
|
||||
}
|
||||
else if ( valueConverter instanceof OrdinalEnumValueConverter ) {
|
||||
@ -132,35 +135,48 @@ public static <E extends Enum<E>> EnumeratedValueResolution<E,?> fromName(
|
||||
final Class<E> enumClass = resolveEnumClass( parts[1], context.getBootstrapContext() );
|
||||
final jakarta.persistence.EnumType style = jakarta.persistence.EnumType.valueOf( parts[ 2 ] );
|
||||
|
||||
//noinspection unchecked,rawtypes
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
final EnumJavaType<E> enumJavaType = (EnumJavaType) javaTypeRegistry.getDescriptor( enumClass );
|
||||
final JdbcType jdbcType;
|
||||
final JdbcType jdbcType = enumJavaType.getRecommendedJdbcType( jdbcTypeIndicators );
|
||||
final EnumValueConverter<E,?> converter;
|
||||
|
||||
if ( style == EnumType.ORDINAL ) {
|
||||
jdbcType = jdbcTypeRegistry.getDescriptor( enumJavaType.hasManyValues() ? SMALLINT : TINYINT );
|
||||
|
||||
final JavaType<Integer> jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
jdbcTypeIndicators.getColumnPrecision(),
|
||||
jdbcTypeIndicators.getColumnScale(),
|
||||
typeConfiguration
|
||||
);
|
||||
converter = new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, jdbcJavaType );
|
||||
}
|
||||
else if ( style == EnumType.STRING ) {
|
||||
jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeIndicators.getColumnLength() == 1 ? CHAR : VARCHAR );
|
||||
final JavaType<String> jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
jdbcTypeIndicators.getColumnPrecision(),
|
||||
jdbcTypeIndicators.getColumnScale(),
|
||||
typeConfiguration
|
||||
);
|
||||
converter = new NamedEnumValueConverter<>( enumJavaType, jdbcType, jdbcJavaType );
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException( );
|
||||
switch ( style ) {
|
||||
case ORDINAL:
|
||||
final JavaType<Integer> integerJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
jdbcTypeIndicators.getColumnPrecision(),
|
||||
jdbcTypeIndicators.getColumnScale(),
|
||||
typeConfiguration
|
||||
);
|
||||
converter = new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, integerJavaType );
|
||||
break;
|
||||
case STRING:
|
||||
if (jdbcType instanceof NativeEnumJdbcType) {
|
||||
converter = new NativeEnumValueConverter<>( enumJavaType, jdbcType, new ObjectJavaType() {
|
||||
@Override
|
||||
public <X> X unwrap(Object value, Class<X> type, WrapperOptions options) {
|
||||
if ( String.class.equals(type) && value instanceof Enum) {
|
||||
return (X) ((Enum<?>) value).name();
|
||||
}
|
||||
else {
|
||||
return super.unwrap(value, type, options);
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
else {
|
||||
final JavaType<String> stringJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
jdbcTypeIndicators.getColumnPrecision(),
|
||||
jdbcTypeIndicators.getColumnScale(),
|
||||
typeConfiguration
|
||||
);
|
||||
converter = new NamedEnumValueConverter<>( enumJavaType, jdbcType, stringJavaType );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( );
|
||||
}
|
||||
|
||||
return new EnumeratedValueResolution<>( jdbcType, converter, context );
|
||||
return new EnumeratedValueResolution<>( jdbcType, converter );
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> Class<E> resolveEnumClass(String enumClassName, BootstrapContext bootstrapContext) {
|
||||
|
@ -23,18 +23,23 @@
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.SerializableType;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.converter.internal.NamedEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.internal.NativeEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeHelper;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
import org.hibernate.type.descriptor.java.ObjectJavaType;
|
||||
import org.hibernate.type.descriptor.java.SerializableJavaType;
|
||||
import org.hibernate.type.descriptor.java.TemporalJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.descriptor.jdbc.NativeEnumJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
@ -43,6 +48,7 @@
|
||||
|
||||
import static org.hibernate.type.SqlTypes.SMALLINT;
|
||||
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||
import static org.hibernate.type.SqlTypes.isCharacterType;
|
||||
|
||||
/**
|
||||
* BasicValue.Resolution resolver for cases where no explicit
|
||||
@ -331,28 +337,6 @@ public static <E extends Enum<E>, R> EnumeratedValueResolution<E,R> fromEnum(
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
MetadataBuildingContext context) {
|
||||
final EnumType enumStyle = stdIndicators.getEnumeratedType();
|
||||
|
||||
if ( enumStyle == EnumType.STRING ) {
|
||||
//noinspection unchecked
|
||||
return (EnumeratedValueResolution<E, R>) stringEnumValueResolution(
|
||||
enumJavaType,
|
||||
explicitJavaType,
|
||||
explicitJdbcType,
|
||||
stdIndicators,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
if ( enumStyle == EnumType.ORDINAL ) {
|
||||
//noinspection unchecked
|
||||
return (EnumeratedValueResolution<E, R>) ordinalEnumValueResolution(
|
||||
enumJavaType,
|
||||
(BasicJavaType<? extends Number>)explicitJavaType,
|
||||
explicitJdbcType,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
if ( enumStyle == null ) {
|
||||
// NOTE : separate from the explicit ORDINAL check to facilitate
|
||||
// handling native database enum types. In theory anyway - atm
|
||||
@ -367,8 +351,27 @@ public static <E extends Enum<E>, R> EnumeratedValueResolution<E,R> fromEnum(
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
throw new MappingException( "Unknown enumeration-style (JPA EnumType) : " + enumStyle );
|
||||
switch ( enumStyle ) {
|
||||
case STRING:
|
||||
//noinspection unchecked
|
||||
return (EnumeratedValueResolution<E, R>) stringEnumValueResolution(
|
||||
enumJavaType,
|
||||
explicitJavaType,
|
||||
explicitJdbcType,
|
||||
stdIndicators,
|
||||
context
|
||||
);
|
||||
case ORDINAL:
|
||||
//noinspection unchecked
|
||||
return (EnumeratedValueResolution<E, R>) ordinalEnumValueResolution(
|
||||
enumJavaType,
|
||||
(BasicJavaType<? extends Number>)explicitJavaType,
|
||||
explicitJdbcType,
|
||||
context
|
||||
);
|
||||
default:
|
||||
throw new MappingException( "Unknown enumeration-style (JPA EnumType) : " + enumStyle );
|
||||
}
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>, N extends Number> EnumeratedValueResolution<E,N> ordinalEnumValueResolution(
|
||||
@ -378,11 +381,9 @@ private static <E extends Enum<E>, N extends Number> EnumeratedValueResolution<E
|
||||
MetadataBuildingContext context) {
|
||||
final JdbcType jdbcType = ordinalJdbcType( explicitJdbcType, enumJavaType, context );
|
||||
final JavaType<N> relationalJavaType = ordinalJavaType( explicitJavaType, jdbcType, context );
|
||||
|
||||
return new EnumeratedValueResolution<>(
|
||||
jdbcType,
|
||||
new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, relationalJavaType ),
|
||||
context
|
||||
new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, relationalJavaType )
|
||||
);
|
||||
}
|
||||
|
||||
@ -418,7 +419,7 @@ private static <N extends Number> JavaType<N> ordinalJavaType(
|
||||
}
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> EnumeratedValueResolution<E,String> stringEnumValueResolution(
|
||||
private static <E extends Enum<E>> EnumeratedValueResolution<E,?> stringEnumValueResolution(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
BasicJavaType<?> explicitJavaType,
|
||||
JdbcType explicitJdbcType,
|
||||
@ -427,19 +428,28 @@ private static <E extends Enum<E>> EnumeratedValueResolution<E,String> stringEnu
|
||||
final JdbcType jdbcType = explicitJdbcType == null
|
||||
? enumJavaType.getRecommendedJdbcType( stdIndicators )
|
||||
: explicitJdbcType;
|
||||
final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, context );
|
||||
|
||||
return new EnumeratedValueResolution<>(
|
||||
jdbcType,
|
||||
new NamedEnumValueConverter<>( enumJavaType, jdbcType, relationalJtd ),
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
private static JdbcType stringJdbcType(JdbcType explicitJdbcType, JdbcTypeIndicators stdIndicators, JavaType<String> relationalJtd) {
|
||||
return explicitJdbcType != null
|
||||
? explicitJdbcType
|
||||
: relationalJtd.getRecommendedJdbcType( stdIndicators );
|
||||
final EnumValueConverter<E,?> converter;
|
||||
if ( jdbcType instanceof NativeEnumJdbcType ) {
|
||||
converter = new NativeEnumValueConverter<>( enumJavaType, jdbcType, new ObjectJavaType() {
|
||||
@Override
|
||||
public <X> X unwrap(Object value, Class<X> type, WrapperOptions options) {
|
||||
if ( String.class.equals(type) && value instanceof Enum) {
|
||||
return (X) ((Enum<?>) value).name();
|
||||
}
|
||||
else {
|
||||
return super.unwrap(value, type, options);
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
else {
|
||||
converter = new NamedEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
jdbcType,
|
||||
stringJavaType( explicitJavaType, stdIndicators, context )
|
||||
);
|
||||
}
|
||||
return new EnumeratedValueResolution<>( jdbcType, converter );
|
||||
}
|
||||
|
||||
private static JavaType<String> stringJavaType(
|
||||
|
@ -18,6 +18,27 @@ public class NamedAuxiliaryDatabaseObject
|
||||
implements Exportable {
|
||||
private final String name;
|
||||
|
||||
public NamedAuxiliaryDatabaseObject(
|
||||
String name,
|
||||
Namespace namespace,
|
||||
String[] createStrings,
|
||||
String[] dropStrings,
|
||||
Set<String> dialectScopes) {
|
||||
super( namespace, createStrings, dropStrings, dialectScopes );
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NamedAuxiliaryDatabaseObject(
|
||||
String name,
|
||||
Namespace namespace,
|
||||
String[] createStrings,
|
||||
String[] dropStrings,
|
||||
Set<String> dialectScopes,
|
||||
boolean beforeTables) {
|
||||
super( namespace, createStrings, dropStrings, dialectScopes, beforeTables );
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NamedAuxiliaryDatabaseObject(
|
||||
String name,
|
||||
Namespace namespace,
|
||||
@ -28,6 +49,17 @@ public NamedAuxiliaryDatabaseObject(
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NamedAuxiliaryDatabaseObject(
|
||||
String name,
|
||||
Namespace namespace,
|
||||
String createString,
|
||||
String dropString,
|
||||
Set<String> dialectScopes,
|
||||
boolean beforeTables) {
|
||||
super( namespace, createString, dropString, dialectScopes, beforeTables );
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExportIdentifier() {
|
||||
return new QualifiedNameImpl(
|
||||
|
@ -30,6 +30,41 @@ public class SimpleAuxiliaryDatabaseObject extends AbstractAuxiliaryDatabaseObje
|
||||
private final String[] createStrings;
|
||||
private final String[] dropStrings;
|
||||
|
||||
private static String extractName(Identifier identifier) {
|
||||
return identifier == null ? null : identifier.getText();
|
||||
}
|
||||
|
||||
public SimpleAuxiliaryDatabaseObject(
|
||||
Namespace namespace,
|
||||
String createString,
|
||||
String dropString,
|
||||
Set<String> dialectScopes,
|
||||
boolean beforeTables) {
|
||||
this(
|
||||
namespace,
|
||||
new String[] { createString },
|
||||
new String[] { dropString },
|
||||
dialectScopes,
|
||||
beforeTables
|
||||
);
|
||||
}
|
||||
|
||||
public SimpleAuxiliaryDatabaseObject(
|
||||
Namespace namespace,
|
||||
String[] createStrings,
|
||||
String[] dropStrings,
|
||||
Set<String> dialectScopes,
|
||||
boolean beforeTables) {
|
||||
this(
|
||||
dialectScopes,
|
||||
extractName( namespace.getPhysicalName().getCatalog() ),
|
||||
extractName( namespace.getPhysicalName().getSchema() ),
|
||||
createStrings,
|
||||
dropStrings,
|
||||
beforeTables
|
||||
);
|
||||
}
|
||||
|
||||
public SimpleAuxiliaryDatabaseObject(
|
||||
Namespace namespace,
|
||||
String createString,
|
||||
@ -57,8 +92,18 @@ public SimpleAuxiliaryDatabaseObject(
|
||||
);
|
||||
}
|
||||
|
||||
private static String extractName(Identifier identifier) {
|
||||
return identifier == null ? null : identifier.getText();
|
||||
public SimpleAuxiliaryDatabaseObject(
|
||||
Set<String> dialectScopes,
|
||||
String catalogName,
|
||||
String schemaName,
|
||||
String[] createStrings,
|
||||
String[] dropStrings,
|
||||
boolean beforeTables) {
|
||||
super( beforeTables, dialectScopes );
|
||||
this.catalogName = catalogName;
|
||||
this.schemaName = schemaName;
|
||||
this.createStrings = createStrings;
|
||||
this.dropStrings = dropStrings;
|
||||
}
|
||||
|
||||
public SimpleAuxiliaryDatabaseObject(
|
||||
|
@ -199,6 +199,7 @@
|
||||
|
||||
import static java.lang.Math.ceil;
|
||||
import static java.lang.Math.log;
|
||||
import static java.util.Arrays.sort;
|
||||
import static org.hibernate.cfg.AvailableSettings.NON_CONTEXTUAL_LOB_CREATION;
|
||||
import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_GET_GENERATED_KEYS;
|
||||
@ -247,6 +248,7 @@
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
|
||||
import static org.hibernate.type.descriptor.converter.internal.EnumHelper.getEnumeratedValues;
|
||||
|
||||
/**
|
||||
* Represents a dialect of SQL implemented by a particular RDBMS. Every
|
||||
@ -736,6 +738,10 @@ public int resolveSqlTypeLength(
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNativeEnums() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this database has a special MySQL-style {@code enum} column type,
|
||||
* return the type declaration for the given enumeration of values.
|
||||
@ -745,10 +751,26 @@ public int resolveSqlTypeLength(
|
||||
* @param values the enumerated values of the type
|
||||
* @return the DDL column type declaration
|
||||
*/
|
||||
public String getEnumTypeDeclaration(String[] values) {
|
||||
public String getEnumTypeDeclaration(String name, String[] values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getEnumTypeDeclaration(Class<? extends Enum<?>> enumType) {
|
||||
String[] values = getEnumeratedValues( enumType );
|
||||
sort( values ); //sort alphabetically, to guarantee alphabetical ordering in queries with 'order by'
|
||||
return getEnumTypeDeclaration( enumType.getSimpleName(), values );
|
||||
}
|
||||
|
||||
public String[] getCreateEnumTypeCommand(String name, String[] values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String[] getCreateEnumTypeCommand(Class<? extends Enum<?>> enumType) {
|
||||
String[] values = getEnumeratedValues( enumType );
|
||||
sort( values ); //sort alphabetically, to guarantee alphabetical ordering in queries with 'order by'
|
||||
return getCreateEnumTypeCommand( enumType.getSimpleName(), values );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value
|
||||
* by its {@linkplain jakarta.persistence.EnumType#STRING string representation}.
|
||||
@ -766,6 +788,10 @@ public String getCheckCondition(String columnName, String[] values) {
|
||||
return check.append( ')' ).toString();
|
||||
}
|
||||
|
||||
public String getCheckCondition(String columnName, Class<? extends Enum<?>> enumType) {
|
||||
return getCheckCondition( columnName, getEnumeratedValues( enumType ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value.
|
||||
* by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}.
|
||||
|
@ -217,8 +217,8 @@ public int resolveSqlTypeLength(
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnumTypeDeclaration(String[] values) {
|
||||
return wrapped.getEnumTypeDeclaration( values );
|
||||
public String getEnumTypeDeclaration(String name, String[] values) {
|
||||
return wrapped.getEnumTypeDeclaration( name, values );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -870,7 +870,7 @@ public boolean supportsColumnCheck() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnumTypeDeclaration(String[] values) {
|
||||
public String getEnumTypeDeclaration(String name, String[] values) {
|
||||
StringBuilder type = new StringBuilder();
|
||||
type.append( "enum (" );
|
||||
String separator = "";
|
||||
|
@ -384,6 +384,43 @@ protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration ty
|
||||
return super.resolveSqlTypeCode( columnTypeName, typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNativeEnums() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnumTypeDeclaration(String name, String[] values) {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, Class<? extends Enum<?>> enumType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String[] getCreateEnumTypeCommand(String name, String[] values) {
|
||||
StringBuilder type = new StringBuilder();
|
||||
type.append( "create type " )
|
||||
.append( name )
|
||||
.append( " as enum (" );
|
||||
String separator = "";
|
||||
for ( String value : values ) {
|
||||
type.append( separator ).append('\'').append( value ).append('\'');
|
||||
separator = ",";
|
||||
}
|
||||
type.append( ')' );
|
||||
StringBuilder cast1 = new StringBuilder();
|
||||
cast1.append("create cast (varchar as " )
|
||||
.append( name )
|
||||
.append( ") with inout as implicit" );
|
||||
StringBuilder cast2 = new StringBuilder();
|
||||
cast2.append("create cast (" )
|
||||
.append( name )
|
||||
.append( " as varchar) with inout as implicit" );
|
||||
return new String[] { type.toString(), cast1.toString(), cast2.toString() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTime() {
|
||||
return "localtime";
|
||||
|
@ -27,6 +27,8 @@
|
||||
import org.hibernate.boot.model.process.internal.NamedConverterResolution;
|
||||
import org.hibernate.boot.model.process.internal.UserTypeResolution;
|
||||
import org.hibernate.boot.model.process.internal.VersionResolution;
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||
@ -318,6 +320,19 @@ public Resolution<?> resolve() {
|
||||
resolveColumn( (Column) selectable, getDialect() );
|
||||
}
|
||||
|
||||
Database database = getBuildingContext().getMetadataCollector().getDatabase();
|
||||
BasicValueConverter<?, ?> valueConverter = resolution.getValueConverter();
|
||||
if ( valueConverter != null ) {
|
||||
AuxiliaryDatabaseObject udt = valueConverter.getAuxiliaryDatabaseObject(
|
||||
resolution.getJdbcType(),
|
||||
database.getDialect(),
|
||||
database.getDefaultNamespace()
|
||||
);
|
||||
if ( udt != null ) {
|
||||
database.addAuxiliaryDatabaseObject( udt );
|
||||
}
|
||||
}
|
||||
|
||||
return resolution;
|
||||
}
|
||||
|
||||
@ -333,7 +348,7 @@ private void resolveColumn(Column column, Dialect dialect) {
|
||||
}
|
||||
|
||||
if ( resolution.getValueConverter() != null ) {
|
||||
final String declaration = resolution.getLegacyResolvedBasicType().getSpecializedTypeDeclaration(dialect);
|
||||
final String declaration = resolution.getLegacyResolvedBasicType().getSpecializedTypeDeclaration( dialect );
|
||||
if ( declaration != null ) {
|
||||
column.setSpecializedTypeDeclaration( declaration );
|
||||
}
|
||||
|
@ -63,6 +63,7 @@
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
|
||||
import static org.hibernate.boot.model.convert.spi.ConverterDescriptor.TYPE_NAME_PREFIX;
|
||||
import static org.hibernate.id.factory.internal.IdentifierGeneratorUtil.createLegacyIdentifierGenerator;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.toBooleanArray;
|
||||
|
||||
@ -208,7 +209,7 @@ protected void justAddColumn(Column column) {
|
||||
protected void justAddColumn(Column column, boolean insertable, boolean updatable) {
|
||||
int index = columns.indexOf( column );
|
||||
if ( index == -1 ) {
|
||||
columns.add(column);
|
||||
columns.add( column );
|
||||
insertability.add( insertable );
|
||||
updatability.add( updatable );
|
||||
}
|
||||
@ -295,8 +296,8 @@ public String getTypeName() {
|
||||
}
|
||||
|
||||
public void setTypeName(String typeName) {
|
||||
if ( typeName != null && typeName.startsWith( ConverterDescriptor.TYPE_NAME_PREFIX ) ) {
|
||||
final String converterClassName = typeName.substring( ConverterDescriptor.TYPE_NAME_PREFIX.length() );
|
||||
if ( typeName != null && typeName.startsWith( TYPE_NAME_PREFIX ) ) {
|
||||
final String converterClassName = typeName.substring( TYPE_NAME_PREFIX.length() );
|
||||
final ClassLoaderService cls = getMetadata()
|
||||
.getMetadataBuildingOptions()
|
||||
.getServiceRegistry()
|
||||
@ -771,7 +772,7 @@ public Dialect getDialect() {
|
||||
|
||||
// todo : cache the AttributeConverterTypeAdapter in case that AttributeConverter is applied multiple times.
|
||||
return new ConvertedBasicTypeImpl<>(
|
||||
ConverterDescriptor.TYPE_NAME_PREFIX
|
||||
TYPE_NAME_PREFIX
|
||||
+ jpaAttributeConverter.getConverterJavaType().getJavaType().getTypeName(),
|
||||
String.format(
|
||||
"BasicType adapter for AttributeConverter<%s,%s>",
|
||||
@ -822,11 +823,11 @@ protected static boolean isSame(Value v1, Value v2) {
|
||||
|
||||
public boolean isSame(SimpleValue other) {
|
||||
return Objects.equals( columns, other.columns )
|
||||
&& Objects.equals( typeName, other.typeName )
|
||||
&& Objects.equals( typeParameters, other.typeParameters )
|
||||
&& Objects.equals( table, other.table )
|
||||
&& Objects.equals( foreignKeyName, other.foreignKeyName )
|
||||
&& Objects.equals( foreignKeyDefinition, other.foreignKeyDefinition );
|
||||
&& Objects.equals( typeName, other.typeName )
|
||||
&& Objects.equals( typeParameters, other.typeParameters )
|
||||
&& Objects.equals( table, other.table )
|
||||
&& Objects.equals( foreignKeyName, other.foreignKeyName )
|
||||
&& Objects.equals( foreignKeyDefinition, other.foreignKeyDefinition );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -151,23 +151,27 @@ private static void appendColumnDefinition(
|
||||
Table table,
|
||||
Metadata metadata,
|
||||
Dialect dialect) {
|
||||
final String columnType = column.getSqlType(metadata);
|
||||
if ( isIdentityColumn(column, table, metadata, dialect) ) {
|
||||
if ( isIdentityColumn( column, table, metadata, dialect) ) {
|
||||
// to support dialects that have their own identity data type
|
||||
if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) {
|
||||
definition.append( ' ' ).append( columnType );
|
||||
definition.append( ' ' ).append( column.getSqlType( metadata ) );
|
||||
}
|
||||
final String identityColumnString = dialect.getIdentityColumnSupport()
|
||||
.getIdentityColumnString( column.getSqlTypeCode(metadata) );
|
||||
.getIdentityColumnString( column.getSqlTypeCode( metadata ) );
|
||||
definition.append( ' ' ).append( identityColumnString );
|
||||
}
|
||||
else {
|
||||
final String columnType;
|
||||
if ( column.hasSpecializedTypeDeclaration() ) {
|
||||
definition.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
|
||||
}
|
||||
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
columnType = column.getSpecializedTypeDeclaration();
|
||||
definition.append( ' ' ).append( columnType );
|
||||
}
|
||||
else {
|
||||
columnType = column.getSqlType( metadata );
|
||||
if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
definition.append( ' ' ).append( columnType );
|
||||
}
|
||||
}
|
||||
|
||||
final String defaultValue = column.getDefaultValue();
|
||||
if ( defaultValue != null ) {
|
||||
|
@ -52,7 +52,7 @@ public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect di
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumTypeDeclaration( getValues() );
|
||||
return dialect.getEnumTypeDeclaration( null, getValues() );
|
||||
}
|
||||
|
||||
protected abstract String[] getValues();
|
||||
|
@ -7,16 +7,17 @@
|
||||
package org.hibernate.type.descriptor.converter.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Namespace;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
import static java.util.Arrays.sort;
|
||||
import static org.hibernate.type.descriptor.converter.internal.EnumHelper.getEnumeratedValues;
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
* BasicValueConverter handling the conversion of an enum based on
|
||||
@ -65,19 +66,30 @@ public int getJdbcTypeCode() {
|
||||
|
||||
@Override
|
||||
public String toSqlLiteral(Object value) {
|
||||
//noinspection rawtypes
|
||||
return String.format( Locale.ROOT, "'%s'", ( (Enum) value ).name() );
|
||||
return "'" + ( (Enum<?>) value ).name() + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getCheckCondition( columnName, getEnumeratedValues( getDomainJavaType().getJavaTypeClass() ) );
|
||||
return dialect.getCheckCondition( columnName, getDomainJavaType().getJavaTypeClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
String[] values = getEnumeratedValues( getDomainJavaType().getJavaTypeClass() );
|
||||
sort( values ); //sort alphabetically, to guarantee alphabetical ordering in queries with 'order by'
|
||||
return dialect.getEnumTypeDeclaration( values );
|
||||
return dialect.getEnumTypeDeclaration( getDomainJavaType().getJavaTypeClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuxiliaryDatabaseObject getAuxiliaryDatabaseObject(JdbcType jdbcType, Dialect dialect, Namespace defaultNamespace) {
|
||||
Class<? extends Enum<?>> enumClass = getDomainJavaType().getJavaTypeClass();
|
||||
String name = enumClass.getSimpleName();
|
||||
String[] create = dialect.getCreateEnumTypeCommand( enumClass );
|
||||
if ( create != null ) {
|
||||
String[] drop = new String[] { "drop type " + name + " cascade" }; //TODO: move to Dialect
|
||||
return new NamedAuxiliaryDatabaseObject( name, defaultNamespace, create, drop, emptySet(), true );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.type.descriptor.converter.internal;
|
||||
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Namespace;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.descriptor.converter.spi.EnumValueConverter;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
* BasicValueConverter handling the conversion of an enum based on
|
||||
* JPA {@link jakarta.persistence.EnumType#STRING} strategy (storing the name)
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class NativeEnumValueConverter<E extends Enum<E>> implements EnumValueConverter<E,Object>, Serializable {
|
||||
private final EnumJavaType<E> domainTypeDescriptor;
|
||||
private final JdbcType jdbcType;
|
||||
private final JavaType<Object> relationalTypeDescriptor;
|
||||
|
||||
public NativeEnumValueConverter(
|
||||
EnumJavaType<E> domainTypeDescriptor,
|
||||
JdbcType jdbcType,
|
||||
JavaType<Object> relationalTypeDescriptor) {
|
||||
this.domainTypeDescriptor = domainTypeDescriptor;
|
||||
this.jdbcType = jdbcType;
|
||||
this.relationalTypeDescriptor = relationalTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumJavaType<E> getDomainJavaType() {
|
||||
return domainTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Object> getRelationalJavaType() {
|
||||
return relationalTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E toDomainValue(Object relationalForm) {
|
||||
return relationalForm instanceof String
|
||||
? domainTypeDescriptor.fromName( (String) relationalForm )
|
||||
: (E) relationalForm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toRelationalValue(E domainForm) {
|
||||
return domainForm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return jdbcType.getDefaultSqlTypeCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSqlLiteral(Object value) {
|
||||
return "'" + ( (Enum<?>) value ).name() + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getCheckCondition( columnName, getDomainJavaType().getJavaTypeClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumTypeDeclaration( getDomainJavaType().getJavaTypeClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuxiliaryDatabaseObject getAuxiliaryDatabaseObject(JdbcType jdbcType, Dialect dialect, Namespace defaultNamespace) {
|
||||
Class<? extends Enum<?>> enumClass = getDomainJavaType().getJavaTypeClass();
|
||||
String name = enumClass.getSimpleName();
|
||||
String[] create = dialect.getCreateEnumTypeCommand( enumClass );
|
||||
if ( create != null ) {
|
||||
String[] drop = new String[] { "drop type " + name + " cascade" }; //TODO: move to Dialect
|
||||
return new NamedAuxiliaryDatabaseObject( name, defaultNamespace, create, drop, emptySet(), true );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -62,8 +62,7 @@ public JavaType<N> getRelationalJavaType() {
|
||||
|
||||
@Override
|
||||
public String toSqlLiteral(Object value) {
|
||||
//noinspection rawtypes
|
||||
return Integer.toString( ( (Enum) value ).ordinal() );
|
||||
return Integer.toString( ( (Enum<?>) value ).ordinal() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -7,6 +7,8 @@
|
||||
package org.hibernate.type.descriptor.converter.spi;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Namespace;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
@ -66,4 +68,9 @@ default String getCheckCondition(String columnName, JdbcType sqlType, Dialect di
|
||||
default String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Incubating
|
||||
default AuxiliaryDatabaseObject getAuxiliaryDatabaseObject(JdbcType jdbcType, Dialect dialect, Namespace defaultNamespace) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
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.NativeEnumJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import static jakarta.persistence.EnumType.ORDINAL;
|
||||
@ -36,24 +37,26 @@ public EnumJavaType(Class<T> type) {
|
||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
|
||||
final JdbcTypeRegistry registry = context.getTypeConfiguration().getJdbcTypeRegistry();
|
||||
final EnumType type = context.getEnumeratedType();
|
||||
int sqlType;
|
||||
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 );
|
||||
case ORDINAL:
|
||||
sqlType = hasManyValues() ? SMALLINT : TINYINT;
|
||||
break;
|
||||
case STRING:
|
||||
if ( context.getDialect().hasNativeEnums() ) {
|
||||
return NativeEnumJdbcType.INSTANCE;
|
||||
}
|
||||
return context.isNationalized()
|
||||
? registry.getDescriptor( NVARCHAR )
|
||||
: registry.getDescriptor( VARCHAR );
|
||||
}
|
||||
default: {
|
||||
else if ( context.getColumnLength() == 1 ) {
|
||||
sqlType = context.isNationalized() ? NCHAR : CHAR;
|
||||
}
|
||||
else {
|
||||
sqlType = context.isNationalized() ? NVARCHAR : VARCHAR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new AssertionFailure("unknown EnumType");
|
||||
}
|
||||
}
|
||||
return registry.getDescriptor( sqlType );
|
||||
}
|
||||
|
||||
public boolean hasManyValues() {
|
||||
@ -250,4 +253,5 @@ public boolean isWider(JavaType<?> javaType) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package org.hibernate.type.descriptor.jdbc;
|
||||
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class NativeEnumJdbcType extends VarcharJdbcType {
|
||||
|
||||
public static final NativeEnumJdbcType INSTANCE = new NativeEnumJdbcType();
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
st.setObject( index, value, SqlTypes.OTHER );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
st.setObject( name, value, SqlTypes.OTHER );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
|
||||
return new BasicExtractor<>( javaType, this ) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return (X) rs.getObject( paramIndex );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return (X) statement.getObject( index );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||
return (X) statement.getObject( name );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user