HHH-15847 fix check constraint creation
- fix check constraints for built-in Boolean converters - move getCheckCondition() + getSpecializedTypeDeclaration() from JavaType to BasicValueConverter - simplify the API of Dialect related to check constraints - recover check constraint for boolean on Oracle by letting Dialects register converters - attempt to clean up some generics stuff in enum-related code
This commit is contained in:
parent
a25e53d1ab
commit
4d2f4988c8
|
@ -510,8 +510,14 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
|||
attributeConverterManager.addRegistration( conversion, bootstrapContext );
|
||||
}
|
||||
|
||||
private boolean doneDialect;
|
||||
|
||||
@Override
|
||||
public ConverterAutoApplyHandler getAttributeConverterAutoApplyHandler() {
|
||||
if ( !doneDialect ) {
|
||||
getDatabase().getDialect().registerAttributeConverters( this );
|
||||
doneDialect = true;
|
||||
}
|
||||
return attributeConverterManager;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ public class TypeDefinition implements Serializable {
|
|||
Map<String,String> parameters) {
|
||||
this.name = name;
|
||||
this.typeImplementorClass = typeImplementorClass;
|
||||
this.registrationKeys= registrationKeys;
|
||||
this.registrationKeys = registrationKeys;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import java.util.function.Function;
|
|||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.internal.ClassmateContext;
|
||||
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
|
||||
import org.hibernate.boot.model.convert.spi.ConverterAutoApplyHandler;
|
||||
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.boot.model.convert.internal;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -18,6 +19,10 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
|
|||
import com.fasterxml.classmate.ResolvedType;
|
||||
import com.fasterxml.classmate.members.ResolvedMember;
|
||||
|
||||
import static org.hibernate.boot.model.convert.internal.ConverterHelper.resolveAttributeType;
|
||||
import static org.hibernate.boot.model.convert.internal.ConverterHelper.resolveMember;
|
||||
import static org.hibernate.boot.model.convert.internal.ConverterHelper.typesMatch;
|
||||
|
||||
/**
|
||||
* Standard implementation of AutoApplicableConverterDescriptor
|
||||
*
|
||||
|
@ -34,9 +39,9 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
|
|||
public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(
|
||||
XProperty xProperty,
|
||||
MetadataBuildingContext context) {
|
||||
final ResolvedType attributeType = ConverterHelper.resolveAttributeType( xProperty, context );
|
||||
final ResolvedType attributeType = resolveAttributeType( xProperty, context );
|
||||
|
||||
return ConverterHelper.typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), attributeType )
|
||||
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), attributeType )
|
||||
? linkedConverterDescriptor
|
||||
: null;
|
||||
}
|
||||
|
@ -45,20 +50,32 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
|
|||
public ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(
|
||||
XProperty xProperty,
|
||||
MetadataBuildingContext context) {
|
||||
final ResolvedMember<?> collectionMember = ConverterHelper.resolveMember( xProperty, context );
|
||||
final ResolvedMember<?> collectionMember = resolveMember( xProperty, context );
|
||||
|
||||
final ResolvedType elementType;
|
||||
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
|
||||
elementType = collectionMember.getType().typeParametersFor( Map.class ).get( 1 );
|
||||
Class<?> erasedType = collectionMember.getType().getErasedType();
|
||||
if ( Map.class.isAssignableFrom( erasedType ) ) {
|
||||
List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Map.class);
|
||||
if ( typeArguments.size() < 2 ) {
|
||||
return null;
|
||||
}
|
||||
elementType = typeArguments.get( 1 );
|
||||
}
|
||||
else if ( Collection.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
|
||||
elementType = collectionMember.getType().typeParametersFor( Collection.class ).get( 0 );
|
||||
else if ( Collection.class.isAssignableFrom( erasedType ) ) {
|
||||
List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Collection.class);
|
||||
if ( typeArguments.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
elementType = typeArguments.get( 0 );
|
||||
}
|
||||
else if ( erasedType.isArray() ) {
|
||||
elementType = collectionMember.getType().getArrayElementType();
|
||||
}
|
||||
else {
|
||||
throw new HibernateException( "Attribute was neither a Collection nor a Map : " + collectionMember.getType().getErasedType() );
|
||||
throw new HibernateException( "Attribute was neither a Collection nor a Map : " + erasedType);
|
||||
}
|
||||
|
||||
return ConverterHelper.typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), elementType )
|
||||
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), elementType )
|
||||
? linkedConverterDescriptor
|
||||
: null;
|
||||
}
|
||||
|
@ -68,17 +85,21 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
|
|||
XProperty xProperty,
|
||||
MetadataBuildingContext context) {
|
||||
|
||||
final ResolvedMember<?> collectionMember = ConverterHelper.resolveMember( xProperty, context );
|
||||
final ResolvedMember<?> collectionMember = resolveMember( xProperty, context );
|
||||
final ResolvedType keyType;
|
||||
|
||||
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
|
||||
keyType = collectionMember.getType().typeParametersFor( Map.class ).get( 0 );
|
||||
List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Map.class);
|
||||
if ( typeArguments.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
keyType = typeArguments.get(0);
|
||||
}
|
||||
else {
|
||||
throw new HibernateException( "Attribute was not a Map : " + collectionMember.getType().getErasedType() );
|
||||
}
|
||||
|
||||
return ConverterHelper.typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), keyType )
|
||||
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), keyType )
|
||||
? linkedConverterDescriptor
|
||||
: null;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ public class ClassBasedConverterDescriptor extends AbstractConverterDescriptor {
|
|||
|
||||
@Override
|
||||
protected ManagedBean<? extends AttributeConverter<?, ?>> createManagedBean(JpaAttributeConverterCreationContext context) {
|
||||
//noinspection unchecked
|
||||
return (ManagedBean<? extends AttributeConverter<?, ?>>) context.getManagedBeanRegistry().getBean( getAttributeConverterClass() );
|
||||
return context.getManagedBeanRegistry().getBean( getAttributeConverterClass() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -274,7 +274,8 @@ public class InferredBasicValueResolver {
|
|||
recommendedJtd,
|
||||
explicitJdbcType
|
||||
),
|
||||
recommendedJtd );
|
||||
recommendedJtd
|
||||
);
|
||||
legacyType = jdbcMapping;
|
||||
}
|
||||
else {
|
||||
|
@ -358,9 +359,9 @@ public class InferredBasicValueResolver {
|
|||
}
|
||||
}
|
||||
|
||||
public static <E extends Enum<E>> InferredBasicValueResolution<E,?> fromEnum(
|
||||
public static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E,?> fromEnum(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
BasicJavaType<?> explicitJavaType,
|
||||
BasicJavaType<N> explicitJavaType,
|
||||
JdbcType explicitJdbcType,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
|
@ -383,7 +384,6 @@ public class InferredBasicValueResolver {
|
|||
enumJavaType,
|
||||
explicitJavaType,
|
||||
explicitJdbcType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
@ -393,41 +393,51 @@ public class InferredBasicValueResolver {
|
|||
}
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> InferredBasicValueResolution<E, Number> ordinalEnumValueResolution(
|
||||
private static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E,N> ordinalEnumValueResolution(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
BasicJavaType<?> explicitJavaType,
|
||||
JavaType<N> explicitJavaType,
|
||||
JdbcType explicitJdbcType,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final JavaType<Number> relationalJtd;
|
||||
return ordinalResolution(
|
||||
enumJavaType,
|
||||
integerJavaType( explicitJavaType, typeConfiguration ),
|
||||
integerJdbcType( explicitJdbcType, typeConfiguration ),
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
private static JdbcType integerJdbcType(JdbcType explicitJdbcType, TypeConfiguration typeConfiguration) {
|
||||
return explicitJdbcType != null
|
||||
? explicitJdbcType
|
||||
: typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SMALLINT );
|
||||
}
|
||||
|
||||
private static <N extends Number> JavaType<N> integerJavaType(JavaType<N> explicitJavaType, TypeConfiguration typeConfiguration) {
|
||||
if ( explicitJavaType != null ) {
|
||||
if ( ! Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
|
||||
if ( !Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
|
||||
throw new MappingException(
|
||||
"Explicit JavaType [" + explicitJavaType +
|
||||
"] applied to enumerated value with EnumType#ORDINAL" +
|
||||
" should handle `java.lang.Integer` as its relational type descriptor"
|
||||
);
|
||||
}
|
||||
//noinspection unchecked
|
||||
relationalJtd = (BasicJavaType<Number>) explicitJavaType;
|
||||
return explicitJavaType;
|
||||
}
|
||||
else {
|
||||
relationalJtd = typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class );
|
||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class );
|
||||
}
|
||||
}
|
||||
|
||||
final JdbcType jdbcType = explicitJdbcType != null
|
||||
? explicitJdbcType
|
||||
: typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SMALLINT );
|
||||
|
||||
final OrdinalEnumValueConverter<E> valueConverter = new OrdinalEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
jdbcType,
|
||||
relationalJtd
|
||||
);
|
||||
private static <E extends Enum<E>, N extends Number> InferredBasicValueResolution<E, N> ordinalResolution(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
JavaType<N> relationalJtd,
|
||||
JdbcType jdbcType,
|
||||
TypeConfiguration typeConfiguration
|
||||
) {
|
||||
final CustomType<E> customType = new CustomType<>(
|
||||
new org.hibernate.type.EnumType<>(
|
||||
enumJavaType.getJavaTypeClass(),
|
||||
valueConverter,
|
||||
new OrdinalEnumValueConverter<>( enumJavaType, jdbcType, relationalJtd ),
|
||||
typeConfiguration
|
||||
),
|
||||
typeConfiguration
|
||||
|
@ -448,37 +458,28 @@ public class InferredBasicValueResolver {
|
|||
JdbcType explicitJdbcType,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final JavaType<String> relationalJtd;
|
||||
if ( explicitJavaType != null ) {
|
||||
if ( ! String.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
|
||||
throw new MappingException(
|
||||
"Explicit JavaType [" + explicitJavaType +
|
||||
"] applied to enumerated value with EnumType#STRING" +
|
||||
" should handle `java.lang.String` as its relational type descriptor"
|
||||
);
|
||||
}
|
||||
//noinspection unchecked
|
||||
relationalJtd = (BasicJavaType<String>) explicitJavaType;
|
||||
}
|
||||
else {
|
||||
final boolean useCharacter = stdIndicators.getColumnLength() == 1;
|
||||
relationalJtd = typeConfiguration.getJavaTypeRegistry()
|
||||
.getDescriptor( useCharacter ? Character.class : String.class );
|
||||
}
|
||||
|
||||
final JdbcType jdbcType = explicitJdbcType != null
|
||||
? explicitJdbcType
|
||||
: relationalJtd.getRecommendedJdbcType(stdIndicators);
|
||||
|
||||
final NamedEnumValueConverter<E> valueConverter = new NamedEnumValueConverter<>(
|
||||
final JavaType<String> relationalJtd = stringJavaType( explicitJavaType, stdIndicators, typeConfiguration );
|
||||
return stringResolution(
|
||||
enumJavaType,
|
||||
jdbcType,
|
||||
relationalJtd
|
||||
relationalJtd,
|
||||
stringJdbcType( explicitJdbcType, stdIndicators, relationalJtd ),
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> InferredBasicValueResolution<E, String> stringResolution(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
JavaType<String> relationalJtd,
|
||||
JdbcType jdbcType,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final CustomType<E> customType = new CustomType<>(
|
||||
new org.hibernate.type.EnumType<>(
|
||||
enumJavaType.getJavaTypeClass(),
|
||||
valueConverter,
|
||||
new NamedEnumValueConverter<E>(
|
||||
enumJavaType,
|
||||
jdbcType,
|
||||
relationalJtd
|
||||
),
|
||||
typeConfiguration
|
||||
),
|
||||
typeConfiguration
|
||||
|
@ -493,6 +494,29 @@ public class InferredBasicValueResolver {
|
|||
);
|
||||
}
|
||||
|
||||
private static JdbcType stringJdbcType(JdbcType explicitJdbcType, JdbcTypeIndicators stdIndicators, JavaType<String> relationalJtd) {
|
||||
return explicitJdbcType != null
|
||||
? explicitJdbcType
|
||||
: relationalJtd.getRecommendedJdbcType( stdIndicators );
|
||||
}
|
||||
|
||||
private static JavaType<String> stringJavaType(BasicJavaType<?> explicitJavaType, JdbcTypeIndicators stdIndicators, TypeConfiguration typeConfiguration) {
|
||||
if ( explicitJavaType != null ) {
|
||||
if ( ! String.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
|
||||
throw new MappingException(
|
||||
"Explicit JavaType [" + explicitJavaType +
|
||||
"] applied to enumerated value with EnumType#STRING" +
|
||||
" should handle `java.lang.String` as its relational type descriptor"
|
||||
);
|
||||
}
|
||||
return (JavaType<String>) explicitJavaType;
|
||||
}
|
||||
else {
|
||||
return typeConfiguration.getJavaTypeRegistry()
|
||||
.getDescriptor( stdIndicators.getColumnLength() == 1 ? Character.class : String.class );
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> InferredBasicValueResolution<T,T> fromTemporal(
|
||||
TemporalJavaType<T> reflectedJtd,
|
||||
BasicJavaType<?> explicitJavaType,
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.hibernate.boot.TempTableDdlTransactionHandling;
|
|||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Sequence;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.function.CastFunction;
|
||||
|
@ -215,11 +216,9 @@ import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
|
|||
import static org.hibernate.type.SqlTypes.TINYINT;
|
||||
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||
import static org.hibernate.type.SqlTypes.isCharacterType;
|
||||
import static org.hibernate.type.SqlTypes.isFloatOrRealOrDouble;
|
||||
import static org.hibernate.type.SqlTypes.isIntegral;
|
||||
import static org.hibernate.type.SqlTypes.isNumericOrDecimal;
|
||||
import static org.hibernate.type.SqlTypes.isNumericType;
|
||||
import static org.hibernate.type.SqlTypes.isVarbinaryType;
|
||||
import static org.hibernate.type.SqlTypes.isVarcharType;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
|
||||
|
@ -349,6 +348,8 @@ public abstract class Dialect implements ConversionContext {
|
|||
Boolean.toString( getDefaultUseGetGeneratedKeys() ) );
|
||||
}
|
||||
|
||||
public void registerAttributeConverters(InFlightMetadataCollector metadataCollector) {}
|
||||
|
||||
/**
|
||||
* Register ANSI-standard column types using the length limits defined
|
||||
* by {@link #getMaxVarcharLength()}, {@link #getMaxNVarcharLength()},
|
||||
|
@ -653,50 +654,29 @@ public abstract class Dialect implements ConversionContext {
|
|||
}
|
||||
}
|
||||
|
||||
public String getBooleanTypeDeclaration(int sqlType, char falseChar, char trueChar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents a boolean value.
|
||||
*/
|
||||
public String getBooleanCheckCondition(String columnName, int sqlType, char falseChar, char trueChar) {
|
||||
if ( isCharacterType(sqlType) ) {
|
||||
return columnName + " in ('" + falseChar + "','" + trueChar + "')";
|
||||
}
|
||||
else if ( isNumericType(sqlType) && !supportsBitType() ) {
|
||||
return columnName + " in (0,1)";
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getEnumTypeDeclaration(int sqlType, Class<? extends Enum<?>> enumClass) {
|
||||
public String getEnumTypeDeclaration(String[] values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value.
|
||||
*/
|
||||
public String getEnumCheckCondition(String columnName, int sqlType, Class<? extends Enum<?>> enumClass) {
|
||||
if ( isCharacterType(sqlType) ) {
|
||||
StringBuilder check = new StringBuilder();
|
||||
check.append( columnName ).append( " in (" );
|
||||
String separator = "";
|
||||
for ( Enum<?> value : enumClass.getEnumConstants() ) {
|
||||
check.append( separator ).append('\'').append( value.name() ).append('\'');
|
||||
separator = ",";
|
||||
}
|
||||
return check.append( ')' ).toString();
|
||||
}
|
||||
else if ( isNumericType(sqlType) ) {
|
||||
int last = enumClass.getEnumConstants().length - 1;
|
||||
return columnName + " between 0 and " + last;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
public String getCheckCondition(String columnName, String[] values) {
|
||||
StringBuilder check = new StringBuilder();
|
||||
check.append( columnName ).append( " in (" );
|
||||
String separator = "";
|
||||
for ( String value : values ) {
|
||||
check.append( separator ).append('\'').append( value ).append('\'');
|
||||
separator = ",";
|
||||
}
|
||||
return check.append( ')' ).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value.
|
||||
*/
|
||||
public String getCheckCondition(String columnName, long min, long max) {
|
||||
return columnName + " between " + min + " and " + max;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -77,8 +77,6 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
|||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||
import static org.hibernate.type.SqlTypes.isCharacterType;
|
||||
import static org.hibernate.type.SqlTypes.isIntegral;
|
||||
import static org.hibernate.type.SqlTypes.BIGINT;
|
||||
import static org.hibernate.type.SqlTypes.BINARY;
|
||||
import static org.hibernate.type.SqlTypes.BIT;
|
||||
|
@ -755,39 +753,23 @@ public class MySQLDialect extends Dialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getEnumTypeDeclaration(int sqlType, Class<? extends Enum<?>> enumClass) {
|
||||
if ( isCharacterType(sqlType) || isIntegral(sqlType) ) {
|
||||
StringBuilder type = new StringBuilder();
|
||||
type.append( "enum (" );
|
||||
String separator = "";
|
||||
for ( Enum<?> value : enumClass.getEnumConstants() ) {
|
||||
type.append( separator ).append('\'').append( value.name() ).append('\'');
|
||||
separator = ",";
|
||||
}
|
||||
return type.append( ')' ).toString();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
public String getEnumTypeDeclaration(String[] values) {
|
||||
StringBuilder type = new StringBuilder();
|
||||
type.append( "enum (" );
|
||||
String separator = "";
|
||||
for ( String value : values ) {
|
||||
type.append( separator ).append('\'').append( value ).append('\'');
|
||||
separator = ",";
|
||||
}
|
||||
return type.append( ')' ).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBooleanTypeDeclaration(int sqlType, char falseChar, char trueChar) {
|
||||
return isCharacterType( sqlType ) ? "enum ('" + falseChar + "','" + trueChar + "')" : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEnumCheckCondition(String columnName, int sqlType, Class<? extends Enum<?>> enumClass) {
|
||||
// don't need it, since we're using the 'enum' type
|
||||
public String getCheckCondition(String columnName, String[] values) {
|
||||
//not needed, because we use an 'enum' type
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBooleanCheckCondition(String columnName, int sqlType, char falseChar, char trueChar) {
|
||||
return isCharacterType( sqlType ) ? null
|
||||
: super.getBooleanCheckCondition( columnName, sqlType, falseChar, trueChar );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryHintString(String query, String hints) {
|
||||
return IndexQueryHintHandler.INSTANCE.addQueryHints( query, hints );
|
||||
|
|
|
@ -15,9 +15,12 @@ import java.util.Locale;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.QueryTimeoutException;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.ModeStatsModeEmulation;
|
||||
|
@ -47,6 +50,7 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
|||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
|
@ -74,6 +78,8 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
|||
import org.hibernate.type.JavaObjectType;
|
||||
import org.hibernate.type.NullType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
@ -636,6 +642,48 @@ public class OracleDialect extends Dialect {
|
|||
}
|
||||
}
|
||||
|
||||
@Converter(autoApply = true)
|
||||
static class BooleanBooleanConverter implements AttributeConverter<Boolean,Boolean>, BasicValueConverter<Boolean,Boolean> {
|
||||
@Override
|
||||
public Boolean convertToDatabaseColumn(Boolean attribute) {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(Boolean dbData) {
|
||||
return dbData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean toDomainValue(Boolean relationalForm) {
|
||||
return relationalForm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean toRelationalValue(Boolean domainForm) {
|
||||
return domainForm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getDomainJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getRelationalJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return jdbcType.getDefaultSqlTypeCode() == Types.BOOLEAN ? columnName + " in (0,1)" : null;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAttributeConverters(InFlightMetadataCollector metadataCollector) {
|
||||
metadataCollector.addAttributeConverter( BooleanBooleanConverter.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneSupport getTimeZoneSupport() {
|
||||
return TimeZoneSupport.NATIVE;
|
||||
|
|
|
@ -318,34 +318,35 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
}
|
||||
|
||||
final Selectable selectable = getColumn();
|
||||
if ( selectable instanceof Column && resolution.getValueConverter() == null ) {
|
||||
final Column column = (Column) selectable;
|
||||
|
||||
if ( column.getSqlTypeCode() == null ) {
|
||||
column.setSqlTypeCode( resolution.getJdbcType().getDefaultSqlTypeCode() );
|
||||
}
|
||||
|
||||
final Dialect dialect = getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
column.setSpecializedTypeDeclaration(
|
||||
resolution.getDomainJavaType().getSpecializedTypeDeclaration(
|
||||
resolution.getJdbcType(),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
if ( dialect.supportsColumnCheck() && !column.hasCheckConstraint() ) {
|
||||
column.setCheckConstraint(
|
||||
resolution.getDomainJavaType().getCheckCondition(
|
||||
column.getQuotedName(dialect),
|
||||
resolution.getJdbcType(),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( selectable instanceof Column ) {
|
||||
resolveColumn( (Column) selectable, getServiceRegistry().getService( JdbcServices.class ).getDialect() );
|
||||
}
|
||||
|
||||
return resolution;
|
||||
}
|
||||
|
||||
private void resolveColumn(Column column, Dialect dialect) {
|
||||
|
||||
if ( column.getSqlTypeCode() == null ) {
|
||||
column.setSqlTypeCode( resolution.getJdbcType().getDefaultSqlTypeCode() );
|
||||
}
|
||||
|
||||
if ( resolution.getValueConverter() != null ) {
|
||||
column.setSpecializedTypeDeclaration( resolution.getValueConverter().getSpecializedTypeDeclaration( resolution.getJdbcType(), dialect ) );
|
||||
|
||||
if ( dialect.supportsColumnCheck() && !column.hasCheckConstraint() ) {
|
||||
column.setCheckConstraint(
|
||||
resolution.getValueConverter()
|
||||
.getCheckCondition(
|
||||
column.getQuotedName( dialect ),
|
||||
resolution.getJdbcType(),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Resolution<?> buildResolution() {
|
||||
Properties typeParameters = getTypeParameters();
|
||||
if ( typeParameters != null
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.metamodel.model.convert.internal;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class EnumHelper {
|
||||
public static final String[] getEnumeratedValues(Class<? extends Enum<?>> enumClass) {
|
||||
Enum<?>[] values = enumClass.getEnumConstants();
|
||||
String[] names = new String[values.length];
|
||||
for ( int i = 0; i < values.length; i++ ) {
|
||||
names[i] = values[i].name();
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ import jakarta.persistence.AttributeConverter;
|
|||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
|
||||
import org.hibernate.resource.beans.spi.ManagedBean;
|
||||
import org.hibernate.type.descriptor.converter.AttributeConverterMutabilityPlanImpl;
|
||||
|
@ -17,6 +19,7 @@ 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.java.spi.RegistryHelper;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
* Standard implementation of JpaAttributeConverter
|
||||
|
@ -106,6 +109,28 @@ public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType sqlType, Dialect dialect) {
|
||||
if ( BasicValueConverter.class.isAssignableFrom( attributeConverterBean.getBeanClass() ) ) {
|
||||
return ((BasicValueConverter<?, ?>) attributeConverterBean.getBeanInstance())
|
||||
.getCheckCondition( columnName, sqlType, dialect );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
if ( BasicValueConverter.class.isAssignableFrom( attributeConverterBean.getBeanClass() ) ) {
|
||||
return ((BasicValueConverter<?, ?>) attributeConverterBean.getBeanInstance())
|
||||
.getSpecializedTypeDeclaration( jdbcType, dialect );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<? extends AttributeConverter<O, R>> getConverterJavaType() {
|
||||
return converterJtd;
|
||||
|
|
|
@ -13,14 +13,16 @@ import java.sql.PreparedStatement;
|
|||
import java.sql.SQLException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
import static org.hibernate.metamodel.model.convert.internal.EnumHelper.getEnumeratedValues;
|
||||
|
||||
/**
|
||||
* BasicValueConverter handling the conversion of an enum based on
|
||||
* JPA {@link jakarta.persistence.EnumType#STRING} strategy (storing the name)
|
||||
|
@ -32,7 +34,7 @@ public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConv
|
|||
private final JdbcType jdbcType;
|
||||
private final JavaType<String> relationalTypeDescriptor;
|
||||
|
||||
private transient ValueExtractor<String> valueExtractor;
|
||||
// private transient ValueExtractor<String> valueExtractor;
|
||||
private transient ValueBinder<String> valueBinder;
|
||||
|
||||
public NamedEnumValueConverter(
|
||||
|
@ -43,8 +45,8 @@ public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConv
|
|||
this.jdbcType = jdbcType;
|
||||
this.relationalTypeDescriptor = relationalTypeDescriptor;
|
||||
|
||||
this.valueExtractor = jdbcType.getExtractor( relationalTypeDescriptor );
|
||||
this.valueBinder = jdbcType.getBinder( relationalTypeDescriptor );
|
||||
// valueExtractor = jdbcType.getExtractor( relationalTypeDescriptor );
|
||||
valueBinder = jdbcType.getBinder( relationalTypeDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,8 +87,8 @@ public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConv
|
|||
private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
|
||||
stream.defaultReadObject();
|
||||
|
||||
this.valueExtractor = jdbcType.getExtractor( relationalTypeDescriptor );
|
||||
this.valueBinder = jdbcType.getBinder( relationalTypeDescriptor );
|
||||
// valueExtractor = jdbcType.getExtractor( relationalTypeDescriptor );
|
||||
valueBinder = jdbcType.getBinder( relationalTypeDescriptor );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,4 +100,14 @@ public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConv
|
|||
final String jdbcValue = value == null ? null : value.name();
|
||||
valueBinder.bind( statement, jdbcValue, position, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getCheckCondition( columnName, getEnumeratedValues( getDomainJavaType().getJavaTypeClass() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumTypeDeclaration( getEnumeratedValues( getDomainJavaType().getJavaTypeClass() ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ import java.io.Serializable;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
@ -26,25 +26,25 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueConverter<E, Number>, Serializable {
|
||||
public class OrdinalEnumValueConverter<E extends Enum<E>, N extends Number> implements EnumValueConverter<E, N>, Serializable {
|
||||
|
||||
private final EnumJavaType<E> enumJavaType;
|
||||
private final JdbcType jdbcType;
|
||||
private final JavaType<Number> relationalJavaType;
|
||||
private final JavaType<N> relationalJavaType;
|
||||
|
||||
private transient ValueExtractor<Number> valueExtractor;
|
||||
private transient ValueBinder<Number> valueBinder;
|
||||
// private transient ValueExtractor<N> valueExtractor;
|
||||
private transient ValueBinder<N> valueBinder;
|
||||
|
||||
public OrdinalEnumValueConverter(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
JdbcType jdbcType,
|
||||
JavaType<Number> relationalJavaType) {
|
||||
JavaType<N> relationalJavaType) {
|
||||
this.enumJavaType = enumJavaType;
|
||||
this.jdbcType = jdbcType;
|
||||
this.relationalJavaType = relationalJavaType;
|
||||
|
||||
this.valueExtractor = jdbcType.getExtractor( relationalJavaType );
|
||||
this.valueBinder = jdbcType.getBinder( relationalJavaType );
|
||||
// valueExtractor = jdbcType.getExtractor( relationalJavaType );
|
||||
valueBinder = jdbcType.getBinder( relationalJavaType );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,9 +52,9 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
return enumJavaType.fromOrdinal( relationalForm == null ? null : relationalForm.intValue() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number toRelationalValue(E domainForm) {
|
||||
return enumJavaType.toOrdinal( domainForm );
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
public N toRelationalValue(E domainForm) {
|
||||
return (N) enumJavaType.toOrdinal( domainForm );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,7 +68,7 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Number> getRelationalJavaType() {
|
||||
public JavaType<N> getRelationalJavaType() {
|
||||
return relationalJavaType;
|
||||
}
|
||||
|
||||
|
@ -81,8 +81,8 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
|
||||
stream.defaultReadObject();
|
||||
|
||||
this.valueExtractor = jdbcType.getExtractor( relationalJavaType );
|
||||
this.valueBinder = jdbcType.getBinder( relationalJavaType );
|
||||
// valueExtractor = jdbcType.getExtractor( relationalJavaType );
|
||||
valueBinder = jdbcType.getBinder( relationalJavaType );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,4 +90,10 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
throws SQLException {
|
||||
valueBinder.bind( statement, toRelationalValue( value ), position, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
int max = getDomainJavaType().getJavaTypeClass().getEnumConstants().length - 1;
|
||||
return dialect.getCheckCondition( columnName, 0, max );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
package org.hibernate.metamodel.model.convert.spi;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
* Support for basic-value conversions.
|
||||
|
@ -46,4 +48,23 @@ public interface BasicValueConverter<D,R> {
|
|||
* Descriptor for the Java type for the relational portion of this converter
|
||||
*/
|
||||
JavaType<R> getRelationalJavaType();
|
||||
|
||||
/**
|
||||
* The check constraint that should be added to the column
|
||||
* definition in generated DDL.
|
||||
*
|
||||
* @param columnName the name of the column
|
||||
* @param sqlType the {@link JdbcType} of the mapped column
|
||||
* @param dialect the SQL {@link Dialect}
|
||||
* @return a check constraint condition or null
|
||||
*/
|
||||
@Incubating
|
||||
default String getCheckCondition(String columnName, JdbcType sqlType, Dialect dialect) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Incubating
|
||||
default String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,14 @@ import java.sql.PreparedStatement;
|
|||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.Remove;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperationQuery;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
import static org.hibernate.metamodel.model.convert.internal.EnumHelper.getEnumeratedValues;
|
||||
|
||||
/**
|
||||
* BasicValueConverter extension for enum-specific support
|
||||
|
|
|
@ -213,7 +213,7 @@ public class ArgumentTypesValidator implements ArgumentsValidator {
|
|||
private void checkType(int count, String functionName, FunctionParameterType type, int code, Type javaType) {
|
||||
switch (type) {
|
||||
case COMPARABLE:
|
||||
if ( !isCharacterType(code) && !isTemporalType(code) &&!isNumericType(code) && code != UUID ) {
|
||||
if ( !isCharacterType(code) && !isTemporalType(code) && !isNumericType(code) && code != UUID ) {
|
||||
if ( javaType == java.util.UUID.class && ( code == Types.BINARY || isCharacterType( code ) ) ) {
|
||||
// We also consider UUID to be comparable when it's a character or binary type
|
||||
return;
|
||||
|
|
|
@ -6432,20 +6432,29 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SMALLINT );
|
||||
final JavaType<Number> relationalJtd = typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class );
|
||||
|
||||
return new QueryLiteral<>(
|
||||
sqmEnumLiteral.getEnumValue().ordinal(),
|
||||
new CustomType<>(
|
||||
new EnumType<>(
|
||||
enumJtd.getJavaTypeClass(),
|
||||
new OrdinalEnumValueConverter<>( enumJtd, jdbcType, relationalJtd ),
|
||||
typeConfiguration
|
||||
),
|
||||
typeConfiguration
|
||||
)
|
||||
);
|
||||
return queryLiteral( sqmEnumLiteral, enumJtd, typeConfiguration, jdbcType, relationalJtd );
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Enum<T>, N extends Number> QueryLiteral<Integer> queryLiteral(
|
||||
SqmEnumLiteral<?> sqmEnumLiteral,
|
||||
EnumJavaType<T> enumJtd,
|
||||
TypeConfiguration typeConfiguration,
|
||||
JdbcType jdbcType,
|
||||
JavaType<N> relationalJtd) {
|
||||
return new QueryLiteral<>(
|
||||
sqmEnumLiteral.getEnumValue().ordinal(),
|
||||
new CustomType<>(
|
||||
new EnumType<>(
|
||||
enumJtd.getJavaTypeClass(),
|
||||
new OrdinalEnumValueConverter<>( enumJtd, jdbcType, relationalJtd ),
|
||||
typeConfiguration
|
||||
),
|
||||
typeConfiguration
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitFieldLiteral(SqmFieldLiteral<?> sqmFieldLiteral) {
|
||||
return new QueryLiteral<>(
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.CharacterJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
* Abstract supertype of converters which map {@link Boolean} to {@link Character}.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Gavin King
|
||||
*/
|
||||
public abstract class CharBooleanConverter
|
||||
implements AttributeConverter<Boolean, Character>, BasicValueConverter<Boolean, Character> {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
@Override
|
||||
public Character convertToDatabaseColumn(Boolean attribute) {
|
||||
return toRelationalValue( attribute );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(Character dbData) {
|
||||
return toDomainValue( dbData );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getDomainJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Character> getRelationalJavaType() {
|
||||
return CharacterJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getCheckCondition( columnName, getValues() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumTypeDeclaration( getValues() );
|
||||
}
|
||||
|
||||
protected abstract String[] getValues();
|
||||
}
|
|
@ -69,21 +69,21 @@ public class CustomType<J>
|
|||
|
||||
public CustomType(UserType<J> userType, String[] registrationKeys, TypeConfiguration typeConfiguration) throws MappingException {
|
||||
this.userType = userType;
|
||||
this.name = userType.getClass().getName();
|
||||
name = userType.getClass().getName();
|
||||
|
||||
if ( userType instanceof JavaType<?> ) {
|
||||
//noinspection unchecked
|
||||
this.mappedJavaType = (JavaType<J>) userType;
|
||||
mappedJavaType = (JavaType<J>) userType;
|
||||
}
|
||||
else if ( userType instanceof JavaTypedExpressible) {
|
||||
//noinspection unchecked
|
||||
this.mappedJavaType = ( (JavaTypedExpressible<J>) userType ).getExpressibleJavaType();
|
||||
mappedJavaType = ( (JavaTypedExpressible<J>) userType ).getExpressibleJavaType();
|
||||
}
|
||||
else if ( userType instanceof UserVersionType ) {
|
||||
this.mappedJavaType = new UserTypeVersionJavaTypeWrapper<>( (UserVersionType<J>) userType );
|
||||
mappedJavaType = new UserTypeVersionJavaTypeWrapper<>( (UserVersionType<J>) userType );
|
||||
}
|
||||
else {
|
||||
this.mappedJavaType = new UserTypeJavaTypeWrapper<>( userType );
|
||||
mappedJavaType = new UserTypeJavaTypeWrapper<>( userType );
|
||||
}
|
||||
|
||||
final BasicValueConverter<J, Object> valueConverter = userType.getValueConverter();
|
||||
|
@ -91,22 +91,22 @@ public class CustomType<J>
|
|||
// When an explicit value converter is given,
|
||||
// we configure the custom type to use that instead of adapters that delegate to UserType.
|
||||
// This is necessary to support selecting a column with multiple domain type representations.
|
||||
this.jdbcType = userType.getJdbcType( typeConfiguration );
|
||||
this.jdbcJavaType = valueConverter.getRelationalJavaType();
|
||||
jdbcType = userType.getJdbcType( typeConfiguration );
|
||||
jdbcJavaType = valueConverter.getRelationalJavaType();
|
||||
//noinspection unchecked
|
||||
this.valueExtractor = (ValueExtractor<J>) jdbcType.getExtractor( jdbcJavaType );
|
||||
valueExtractor = (ValueExtractor<J>) jdbcType.getExtractor( jdbcJavaType );
|
||||
//noinspection unchecked
|
||||
this.valueBinder = (ValueBinder<J>) jdbcType.getBinder( jdbcJavaType );
|
||||
valueBinder = (ValueBinder<J>) jdbcType.getBinder( jdbcJavaType );
|
||||
//noinspection unchecked
|
||||
this.jdbcLiteralFormatter = (JdbcLiteralFormatter<J>) jdbcType.getJdbcLiteralFormatter( jdbcJavaType );
|
||||
jdbcLiteralFormatter = (JdbcLiteralFormatter<J>) jdbcType.getJdbcLiteralFormatter( jdbcJavaType );
|
||||
}
|
||||
else {
|
||||
// create a JdbcType adapter that uses the UserType binder/extract handling
|
||||
this.jdbcType = new UserTypeSqlTypeAdapter<>( userType, mappedJavaType, typeConfiguration );
|
||||
this.jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||
this.valueExtractor = jdbcType.getExtractor( mappedJavaType );
|
||||
this.valueBinder = jdbcType.getBinder( mappedJavaType );
|
||||
this.jdbcLiteralFormatter = userType instanceof EnhancedUserType
|
||||
jdbcType = new UserTypeSqlTypeAdapter<>( userType, mappedJavaType, typeConfiguration );
|
||||
jdbcJavaType = jdbcType.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||
valueExtractor = jdbcType.getExtractor( mappedJavaType );
|
||||
valueBinder = jdbcType.getBinder( mappedJavaType );
|
||||
jdbcLiteralFormatter = userType instanceof EnhancedUserType
|
||||
? jdbcType.getJdbcLiteralFormatter( mappedJavaType )
|
||||
: null;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.sql.PreparedStatement;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.MapKeyEnumerated;
|
||||
|
@ -33,6 +32,7 @@ import org.hibernate.type.descriptor.ValueBinder;
|
|||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
@ -46,30 +46,10 @@ import org.jboss.logging.Logger;
|
|||
/**
|
||||
* Value type mapper for enumerations.
|
||||
*
|
||||
* Provides 2 distinct forms of "configuration" - one for hbm.xml mapping and
|
||||
* another for annotation/orm.xml mapping triggered within the {@link #setParameterValues}
|
||||
* method
|
||||
*
|
||||
* Annotation based config relies on a {@link ParameterType} reference passed as
|
||||
* an entry in the parameter values under the key {@link #PARAMETER_TYPE}
|
||||
*
|
||||
* hbm.xml based config relies on a number of values from the parameters: <ul>
|
||||
* <li>
|
||||
* {@link #ENUM} - Name the enumeration class.
|
||||
* </li>
|
||||
* <li>
|
||||
* {@link #NAMED} - Should enum be mapped via name. Default is to map as ordinal.
|
||||
* </li>
|
||||
* <li>
|
||||
* {@link #TYPE} - JDBC type code (legacy alternative to {@link #NAMED})
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
* @author Hardy Ferentschik
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EnumType<T extends Enum<T>>
|
||||
implements EnhancedUserType<T>, DynamicParameterizedType, LoggableUserType, TypeConfigurationAware, Serializable {
|
||||
private static final Logger LOG = CoreLogging.logger( EnumType.class );
|
||||
|
@ -90,20 +70,21 @@ public class EnumType<T extends Enum<T>>
|
|||
public EnumType() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EnumType(
|
||||
Class<T> enumClass,
|
||||
EnumValueConverter enumValueConverter,
|
||||
EnumValueConverter<T,?> enumValueConverter,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
this.enumClass = enumClass;
|
||||
this.typeConfiguration = typeConfiguration;
|
||||
|
||||
this.enumValueConverter = enumValueConverter;
|
||||
this.enumValueConverter = (EnumValueConverter<T,Object>) enumValueConverter;
|
||||
this.jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( enumValueConverter.getJdbcTypeCode() );
|
||||
this.jdbcValueExtractor = jdbcType.getExtractor( enumValueConverter.getRelationalJavaType() );
|
||||
this.jdbcValueBinder = jdbcType.getBinder( enumValueConverter.getRelationalJavaType() );
|
||||
this.jdbcValueExtractor = (ValueExtractor<Object>) jdbcType.getExtractor( enumValueConverter.getRelationalJavaType() );
|
||||
this.jdbcValueBinder = (ValueBinder<Object>) jdbcType.getBinder( enumValueConverter.getRelationalJavaType() );
|
||||
}
|
||||
|
||||
public EnumValueConverter getEnumValueConverter() {
|
||||
public EnumValueConverter<T, ?> getEnumValueConverter() {
|
||||
return enumValueConverter;
|
||||
}
|
||||
|
||||
|
@ -117,6 +98,32 @@ public class EnumType<T extends Enum<T>>
|
|||
return enumValueConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An instance of this class is "configured" by a call to {@link #setParameterValues},
|
||||
* where configuration parameters are given as entries in a {@link Properties} object.
|
||||
* There are two distinct ways an instance may be configured:
|
||||
* <ul>
|
||||
* <li>one for {@code hbm.xml}-based mapping, and
|
||||
* <li>another for annotation-based or {@code orm.xml}-based mapping.
|
||||
* </ul>
|
||||
* In the case of annotations or {@code orm.xml}, a {@link ParameterType} is passed to
|
||||
* {@link #setParameterValues} under the key {@value #PARAMETER_TYPE}.
|
||||
* <p>
|
||||
* But in the case of {@code hbm.xml}, there are multiple parameters:
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@value #ENUM}, the name of the Java enumeration class.
|
||||
* </li>
|
||||
* <li>
|
||||
* {@value #NAMED}, specifies if the enum should be mapped by name.
|
||||
* Default is to map as ordinal.
|
||||
* </li>
|
||||
* <li>
|
||||
* {@value #TYPE}, a JDBC type code (legacy alternative to {@value #NAMED}).
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public void setParameterValues(Properties parameters) {
|
||||
// IMPL NOTE: we handle 2 distinct cases here:
|
||||
|
@ -126,74 +133,15 @@ public class EnumType<T extends Enum<T>>
|
|||
// 2) we are not passed a ParameterType - generally this indicates a hbm.xml binding case.
|
||||
final ParameterType reader = (ParameterType) parameters.get( PARAMETER_TYPE );
|
||||
|
||||
// the `reader != null` block handles annotations, while the `else` block
|
||||
// handles hbm.xml
|
||||
// the `reader != null` block handles annotations, while the `else` block handles hbm.xml
|
||||
if ( reader != null ) {
|
||||
enumClass = (Class<T>) reader.getReturnedClass().asSubclass( Enum.class );
|
||||
|
||||
final Long columnLength = reader.getColumnLengths()[0];
|
||||
|
||||
final boolean isOrdinal;
|
||||
final jakarta.persistence.EnumType enumType = getEnumType( reader );
|
||||
if ( enumType == null ) {
|
||||
isOrdinal = true;
|
||||
}
|
||||
else if ( jakarta.persistence.EnumType.ORDINAL.equals( enumType ) ) {
|
||||
isOrdinal = true;
|
||||
}
|
||||
else if ( jakarta.persistence.EnumType.STRING.equals( enumType ) ) {
|
||||
isOrdinal = false;
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure( "Unknown EnumType: " + enumType );
|
||||
}
|
||||
|
||||
final EnumJavaType enumJavaType = (EnumJavaType) typeConfiguration
|
||||
.getJavaTypeRegistry()
|
||||
.getDescriptor( enumClass );
|
||||
|
||||
final LocalJdbcTypeIndicators indicators = new LocalJdbcTypeIndicators(
|
||||
enumType,
|
||||
columnLength,
|
||||
reader
|
||||
);
|
||||
|
||||
final BasicJavaType<?> relationalJavaType = resolveRelationalJavaType(
|
||||
indicators,
|
||||
enumJavaType
|
||||
);
|
||||
|
||||
this.jdbcType = relationalJavaType.getRecommendedJdbcType( indicators );
|
||||
|
||||
if ( isOrdinal ) {
|
||||
this.enumValueConverter = new OrdinalEnumValueConverter(
|
||||
enumJavaType,
|
||||
jdbcType,
|
||||
relationalJavaType
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.enumValueConverter = new NamedEnumValueConverter(
|
||||
enumJavaType,
|
||||
jdbcType,
|
||||
relationalJavaType
|
||||
);
|
||||
}
|
||||
configureUsingReader( reader );
|
||||
}
|
||||
else {
|
||||
final String enumClassName = (String) parameters.get( ENUM );
|
||||
try {
|
||||
enumClass = ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class );
|
||||
}
|
||||
catch ( ClassNotFoundException exception ) {
|
||||
throw new HibernateException( "Enum class not found: " + enumClassName, exception );
|
||||
}
|
||||
|
||||
this.enumValueConverter = interpretParameters( parameters );
|
||||
this.jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( enumValueConverter.getJdbcTypeCode() );
|
||||
configureUsingParameters( parameters );
|
||||
}
|
||||
this.jdbcValueExtractor = jdbcType.getExtractor( enumValueConverter.getRelationalJavaType() );
|
||||
this.jdbcValueBinder = jdbcType.getBinder( enumValueConverter.getRelationalJavaType() );
|
||||
jdbcValueExtractor = jdbcType.getExtractor( enumValueConverter.getRelationalJavaType() );
|
||||
jdbcValueBinder = jdbcType.getBinder( enumValueConverter.getRelationalJavaType() );
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf(
|
||||
|
@ -204,14 +152,54 @@ public class EnumType<T extends Enum<T>>
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void configureUsingParameters(Properties parameters) {
|
||||
final String enumClassName = (String) parameters.get( ENUM );
|
||||
try {
|
||||
enumClass = ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class );
|
||||
}
|
||||
catch ( ClassNotFoundException exception ) {
|
||||
throw new HibernateException( "Enum class not found: " + enumClassName, exception );
|
||||
}
|
||||
enumValueConverter = (EnumValueConverter<T,Object>) interpretParameters( parameters ); //this typecast is rubbish
|
||||
jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( enumValueConverter.getJdbcTypeCode() );
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
private void configureUsingReader(ParameterType reader) {
|
||||
enumClass = (Class<T>) reader.getReturnedClass().asSubclass( Enum.class );
|
||||
final jakarta.persistence.EnumType enumType = getEnumType( reader );
|
||||
final JavaType<T> descriptor = typeConfiguration.getJavaTypeRegistry().getDescriptor( enumClass );
|
||||
final EnumJavaType<T> enumJavaType = (EnumJavaType<T>) descriptor;
|
||||
final LocalJdbcTypeIndicators indicators = new LocalJdbcTypeIndicators( enumType, reader );
|
||||
final JavaType<?> relationalJavaType = resolveRelationalJavaType( indicators, enumJavaType );
|
||||
jdbcType = relationalJavaType.getRecommendedJdbcType( indicators );
|
||||
enumValueConverter = isOrdinal( enumType )
|
||||
? new OrdinalEnumValueConverter( enumJavaType, jdbcType, relationalJavaType )
|
||||
: new NamedEnumValueConverter( enumJavaType, jdbcType, relationalJavaType );
|
||||
}
|
||||
|
||||
private static boolean isOrdinal(jakarta.persistence.EnumType enumType) {
|
||||
if ( enumType == null ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
switch ( enumType ) {
|
||||
case ORDINAL:
|
||||
return true;
|
||||
case STRING:
|
||||
return false;
|
||||
default:
|
||||
throw new AssertionFailure( "Unknown EnumType: " + enumType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BasicJavaType<?> resolveRelationalJavaType(
|
||||
LocalJdbcTypeIndicators indicators,
|
||||
EnumJavaType<?> enumJavaType) {
|
||||
return enumJavaType.getRecommendedJdbcType( indicators ).getJdbcRecommendedJavaTypeMapping(
|
||||
null,
|
||||
null,
|
||||
typeConfiguration
|
||||
);
|
||||
return enumJavaType.getRecommendedJdbcType( indicators )
|
||||
.getJdbcRecommendedJavaTypeMapping( null, null, typeConfiguration );
|
||||
}
|
||||
|
||||
private jakarta.persistence.EnumType getEnumType(ParameterType reader) {
|
||||
|
@ -234,20 +222,19 @@ public class EnumType<T extends Enum<T>>
|
|||
return null;
|
||||
}
|
||||
|
||||
private <A extends Annotation> A getAnnotation(Annotation[] annotations, Class<A> anClass) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private <A extends Annotation> A getAnnotation(Annotation[] annotations, Class<A> annotationType) {
|
||||
for ( Annotation annotation : annotations ) {
|
||||
if ( anClass.isInstance( annotation ) ) {
|
||||
if ( annotationType.isInstance( annotation ) ) {
|
||||
return (A) annotation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private EnumValueConverter<T,Object> interpretParameters(Properties parameters) {
|
||||
//noinspection rawtypes
|
||||
final EnumJavaType enumJavaType = (EnumJavaType) typeConfiguration
|
||||
.getJavaTypeRegistry()
|
||||
.getDescriptor( enumClass );
|
||||
private EnumValueConverter<T,?> interpretParameters(Properties parameters) {
|
||||
JavaType<T> javaType = typeConfiguration.getJavaTypeRegistry().getDescriptor( enumClass );
|
||||
final EnumJavaType<T> enumJavaType = (EnumJavaType<T>) javaType;
|
||||
|
||||
// this method should only be called for hbm.xml handling
|
||||
assert parameters.get( PARAMETER_TYPE ) == null;
|
||||
|
@ -263,66 +250,78 @@ public class EnumType<T extends Enum<T>>
|
|||
-1L,
|
||||
null
|
||||
);
|
||||
final BasicJavaType<?> stringJavaType = (BasicJavaType<?>) typeConfiguration.getJavaTypeRegistry().getDescriptor( String.class );
|
||||
final BasicJavaType<?> integerJavaType = (BasicJavaType<?>) typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class );
|
||||
|
||||
if ( parameters.containsKey( NAMED ) ) {
|
||||
final boolean useNamed = ConfigurationHelper.getBoolean( NAMED, parameters );
|
||||
if ( useNamed ) {
|
||||
//noinspection rawtypes
|
||||
return new NamedEnumValueConverter(
|
||||
enumJavaType,
|
||||
stringJavaType.getRecommendedJdbcType( localIndicators ),
|
||||
stringJavaType
|
||||
);
|
||||
}
|
||||
else {
|
||||
//noinspection rawtypes
|
||||
return new OrdinalEnumValueConverter(
|
||||
enumJavaType,
|
||||
integerJavaType.getRecommendedJdbcType( localIndicators ),
|
||||
typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class )
|
||||
);
|
||||
}
|
||||
return getConverter( enumJavaType, localIndicators, useNamed );
|
||||
}
|
||||
|
||||
if ( parameters.containsKey( TYPE ) ) {
|
||||
final int type = Integer.decode( (String) parameters.get( TYPE ) );
|
||||
if ( isNumericType( type ) ) {
|
||||
//noinspection rawtypes
|
||||
return new OrdinalEnumValueConverter(
|
||||
enumJavaType,
|
||||
integerJavaType.getRecommendedJdbcType( localIndicators ),
|
||||
typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class )
|
||||
);
|
||||
}
|
||||
else if ( isCharacterType( type ) ) {
|
||||
//noinspection rawtypes
|
||||
return new NamedEnumValueConverter(
|
||||
enumJavaType,
|
||||
stringJavaType.getRecommendedJdbcType( localIndicators ),
|
||||
stringJavaType
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Passed JDBC type code [%s] not recognized as numeric nor character",
|
||||
type
|
||||
)
|
||||
);
|
||||
}
|
||||
return getConverterForType( enumJavaType, localIndicators, type );
|
||||
}
|
||||
|
||||
// the fallback
|
||||
return new OrdinalEnumValueConverter(
|
||||
return new OrdinalEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
integerJavaType.getRecommendedJdbcType( localIndicators ),
|
||||
typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class )
|
||||
getIntegerType().getRecommendedJdbcType( localIndicators ),
|
||||
getIntegerType()
|
||||
);
|
||||
}
|
||||
|
||||
private JavaType<Integer> getIntegerType() {
|
||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor(Integer.class);
|
||||
}
|
||||
|
||||
private JavaType<String> getStringType() {
|
||||
return typeConfiguration.getJavaTypeRegistry().getDescriptor(String.class);
|
||||
}
|
||||
|
||||
private EnumValueConverter<T,?> getConverter(
|
||||
EnumJavaType<T> enumJavaType,
|
||||
EnumType<T>.LocalJdbcTypeIndicators localIndicators,
|
||||
boolean useNamed) {
|
||||
if (useNamed) {
|
||||
return new NamedEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
getStringType().getRecommendedJdbcType( localIndicators ),
|
||||
getStringType()
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new OrdinalEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
getIntegerType().getRecommendedJdbcType( localIndicators ),
|
||||
getIntegerType()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private EnumValueConverter<T,?> getConverterForType(
|
||||
EnumJavaType<T> enumJavaType,
|
||||
LocalJdbcTypeIndicators localIndicators,
|
||||
int type) {
|
||||
if ( isNumericType(type) ) {
|
||||
return new OrdinalEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
getIntegerType().getRecommendedJdbcType( localIndicators ),
|
||||
getIntegerType()
|
||||
);
|
||||
}
|
||||
else if ( isCharacterType(type) ) {
|
||||
return new NamedEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
getStringType().getRecommendedJdbcType( localIndicators ),
|
||||
getStringType()
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new HibernateException(
|
||||
String.format( "Passed JDBC type code [%s] not recognized as numeric nor character", type )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCharacterType(int jdbcTypeCode) {
|
||||
switch ( jdbcTypeCode ) {
|
||||
case Types.CHAR:
|
||||
|
@ -377,8 +376,7 @@ public class EnumType<T extends Enum<T>>
|
|||
@Override
|
||||
public T nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException {
|
||||
verifyConfigured();
|
||||
final Object relational = jdbcValueExtractor.extract( rs, position, session );
|
||||
return enumValueConverter.toDomainValue( relational );
|
||||
return enumValueConverter.toDomainValue( jdbcValueExtractor.extract( rs, position, session ) );
|
||||
}
|
||||
|
||||
private void verifyConfigured() {
|
||||
|
@ -446,7 +444,7 @@ public class EnumType<T extends Enum<T>>
|
|||
return enumValueConverter.toDomainValue( enumValueConverter.getRelationalJavaType().fromString( sequence ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
public String toLoggableString(Object value, SessionFactoryImplementor factory) {
|
||||
verifyConfigured();
|
||||
return enumValueConverter.getDomainJavaType().toString( (T) value );
|
||||
|
@ -462,7 +460,11 @@ public class EnumType<T extends Enum<T>>
|
|||
private final Long columnLength;
|
||||
private final ParameterType reader;
|
||||
|
||||
public LocalJdbcTypeIndicators(jakarta.persistence.EnumType enumType, Long columnLength, ParameterType reader) {
|
||||
private LocalJdbcTypeIndicators(jakarta.persistence.EnumType enumType, ParameterType reader) {
|
||||
this( enumType, reader.getColumnLengths()[0], reader );
|
||||
}
|
||||
|
||||
private LocalJdbcTypeIndicators(jakarta.persistence.EnumType enumType, Long columnLength, ParameterType reader) {
|
||||
this.enumType = enumType;
|
||||
this.columnLength = columnLength;
|
||||
this.reader = reader;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.IntegerJavaType;
|
||||
|
@ -13,6 +14,7 @@ import org.hibernate.type.descriptor.java.JavaType;
|
|||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
* Handles conversion to/from Boolean as `0` (false) or `1` (true)
|
||||
|
@ -72,4 +74,9 @@ public class NumericBooleanConverter implements AttributeConverter<Boolean, Inte
|
|||
public JavaType<Integer> getRelationalJavaType() {
|
||||
return IntegerJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return columnName + " in (0,1)";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.CharacterJavaType;
|
||||
|
@ -13,28 +14,24 @@ import org.hibernate.type.descriptor.java.JavaType;
|
|||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
* Handles conversion to/from Boolean as `T` or `F`
|
||||
* Handles conversion to/from {@code Boolean} as {@code 'T'} or {@code 'F'}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Converter
|
||||
public class TrueFalseConverter implements AttributeConverter<Boolean, Character>,
|
||||
BasicValueConverter<Boolean, Character> {
|
||||
public class TrueFalseConverter extends CharBooleanConverter {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final TrueFalseConverter INSTANCE = new TrueFalseConverter();
|
||||
private static final String[] VALUES = {"F", "T"};
|
||||
|
||||
@Override
|
||||
public Character convertToDatabaseColumn(Boolean attribute) {
|
||||
return toRelationalValue( attribute );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(Character dbData) {
|
||||
return toDomainValue( dbData );
|
||||
protected String[] getValues() {
|
||||
return VALUES;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,14 +57,4 @@ public class TrueFalseConverter implements AttributeConverter<Boolean, Character
|
|||
|
||||
return domainForm ? 'T' : 'F';
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getDomainJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Character> getRelationalJavaType() {
|
||||
return CharacterJavaType.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.type;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.CharacterJavaType;
|
||||
|
@ -13,27 +14,24 @@ import org.hibernate.type.descriptor.java.JavaType;
|
|||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
* Handles conversion to/from Boolean as `Y` or `N`
|
||||
* Handles conversion to/from {@code Boolean} as {@code 'Y'} or {@code 'N'}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Converter
|
||||
public class YesNoConverter implements AttributeConverter<Boolean, Character>, BasicValueConverter<Boolean, Character> {
|
||||
public class YesNoConverter extends CharBooleanConverter {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final YesNoConverter INSTANCE = new YesNoConverter();
|
||||
private static final String[] VALUES = {"N", "Y"};
|
||||
|
||||
@Override
|
||||
public Character convertToDatabaseColumn(Boolean attribute) {
|
||||
return toRelationalValue( attribute );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(Character dbData) {
|
||||
return toDomainValue( dbData );
|
||||
protected String[] getValues() {
|
||||
return VALUES;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,14 +58,4 @@ public class YesNoConverter implements AttributeConverter<Boolean, Character>, B
|
|||
|
||||
return domainForm ? 'Y' : 'N';
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getDomainJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Character> getRelationalJavaType() {
|
||||
return CharacterJavaType.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,23 +166,4 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
|
|||
public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getBooleanCheckCondition(
|
||||
columnName,
|
||||
jdbcType.getJdbcTypeCode(),
|
||||
characterValueFalse,
|
||||
characterValueTrue
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getBooleanTypeDeclaration(
|
||||
jdbcType.getJdbcTypeCode(),
|
||||
characterValueFalse,
|
||||
characterValueTrue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.type.descriptor.java;
|
|||
import java.sql.Types;
|
||||
import jakarta.persistence.EnumType;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
@ -214,14 +213,4 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
|
|||
}
|
||||
return Enum.valueOf( getJavaTypeClass(), relationalForm.trim() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCheckCondition(String columnName, JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumCheckCondition( columnName, jdbcType.getJdbcTypeCode(), getJavaTypeClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumTypeDeclaration( jdbcType.getJdbcTypeCode(), getJavaTypeClass() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,6 +267,7 @@ public interface JavaType<T> extends Serializable {
|
|||
return false;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface CoercionContext {
|
||||
TypeConfiguration getTypeConfiguration();
|
||||
}
|
||||
|
@ -276,23 +277,6 @@ public interface JavaType<T> extends Serializable {
|
|||
return (T) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The check constraint that should be added to the column
|
||||
* definition in generated DDL.
|
||||
*
|
||||
* @param columnName the name of the column
|
||||
* @param sqlType the {@link JdbcType} of the mapped column
|
||||
* @param dialect the SQL {@link Dialect}
|
||||
* @return a check constraint condition or null
|
||||
*/
|
||||
default String getCheckCondition(String columnName, JdbcType sqlType, Dialect dialect) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link JavaType} for the given {@link ParameterizedType}
|
||||
* based on this {@link JavaType} registered for the raw type.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.orm.test.schematools;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Convert;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
|
@ -33,6 +34,8 @@ import org.hibernate.tool.schema.spi.ScriptSourceInput;
|
|||
import org.hibernate.tool.schema.spi.ScriptTargetOutput;
|
||||
import org.hibernate.tool.schema.spi.SourceDescriptor;
|
||||
import org.hibernate.tool.schema.spi.TargetDescriptor;
|
||||
import org.hibernate.type.NumericBooleanConverter;
|
||||
import org.hibernate.type.YesNoConverter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -94,7 +97,9 @@ public class EnumCheckTests {
|
|||
}
|
||||
);
|
||||
|
||||
assertThat( CollectingGenerationTarget.commands.get(0).contains( "check ('SOURCE','CLASS','RUNTIME')") );
|
||||
assertThat( CollectingGenerationTarget.commands.get(0) ).contains( "in ('SOURCE','CLASS','RUNTIME')");
|
||||
assertThat( CollectingGenerationTarget.commands.get(0) ).contains( "in ('N','Y')");
|
||||
assertThat( CollectingGenerationTarget.commands.get(0) ).contains( "in (0,1)");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -143,6 +148,10 @@ public class EnumCheckTests {
|
|||
private String name;
|
||||
@Enumerated(EnumType.STRING)
|
||||
RetentionPolicy retentionPolicy;
|
||||
@Convert(converter=YesNoConverter.class)
|
||||
private boolean yesNo;
|
||||
@Convert(converter= NumericBooleanConverter.class)
|
||||
private boolean oneZero;
|
||||
|
||||
private SimpleEntity() {
|
||||
// for use by Hibernate
|
||||
|
|
Loading…
Reference in New Issue