mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-10 13:14:50 +00:00
Automatically generate check constraints for boolean and enum attributes
And make enums map to TINYINT by default.
This commit is contained in:
parent
992b390fce
commit
effec02964
@ -11,7 +11,6 @@
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import javax.persistence.EnumType;
|
import javax.persistence.EnumType;
|
||||||
import javax.persistence.TemporalType;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
@ -26,6 +25,8 @@
|
|||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
|
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
|
||||||
@ -72,7 +73,6 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
|
|||||||
private boolean isNationalized;
|
private boolean isNationalized;
|
||||||
private boolean isLob;
|
private boolean isLob;
|
||||||
private EnumType enumerationStyle;
|
private EnumType enumerationStyle;
|
||||||
private TemporalType temporalPrecision;
|
|
||||||
|
|
||||||
private ConverterDescriptor attributeConverterDescriptor;
|
private ConverterDescriptor attributeConverterDescriptor;
|
||||||
|
|
||||||
@ -191,9 +191,7 @@ public Resolution<?> resolve() {
|
|||||||
() -> {
|
() -> {
|
||||||
if ( resolvedJavaClass != null ) {
|
if ( resolvedJavaClass != null ) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return getBuildingContext().getBootstrapContext()
|
return typeConfiguration.getJavaTypeDescriptorRegistry()
|
||||||
.getTypeConfiguration()
|
|
||||||
.getJavaTypeDescriptorRegistry()
|
|
||||||
.resolveDescriptor( resolvedJavaClass );
|
.resolveDescriptor( resolvedJavaClass );
|
||||||
}
|
}
|
||||||
else if ( ownerName != null && propertyName != null ) {
|
else if ( ownerName != null && propertyName != null ) {
|
||||||
@ -205,8 +203,7 @@ else if ( ownerName != null && propertyName != null ) {
|
|||||||
|
|
||||||
// First resolve from the BasicTypeRegistry.
|
// First resolve from the BasicTypeRegistry.
|
||||||
// If it does resolve, we can use the JTD instead of delegating to the JTD Regsitry.
|
// If it does resolve, we can use the JTD instead of delegating to the JTD Regsitry.
|
||||||
final BasicType basicType = getBuildingContext().getBootstrapContext()
|
final BasicType basicType = typeConfiguration
|
||||||
.getTypeConfiguration()
|
|
||||||
.getBasicTypeRegistry()
|
.getBasicTypeRegistry()
|
||||||
.getRegisteredType( reflectedJavaType.getName() );
|
.getRegisteredType( reflectedJavaType.getName() );
|
||||||
if ( basicType != null ) {
|
if ( basicType != null ) {
|
||||||
@ -214,8 +211,7 @@ else if ( ownerName != null && propertyName != null ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return getBuildingContext().getBootstrapContext()
|
return typeConfiguration
|
||||||
.getTypeConfiguration()
|
|
||||||
.getJavaTypeDescriptorRegistry()
|
.getJavaTypeDescriptorRegistry()
|
||||||
.resolveDescriptor( reflectedJavaType );
|
.resolveDescriptor( reflectedJavaType );
|
||||||
}
|
}
|
||||||
@ -234,6 +230,30 @@ else if ( dependentValue != null ) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( resolution != null
|
||||||
|
//TODO: pass in resolution.getValueConverter()
|
||||||
|
//and use it to determine the range of values!
|
||||||
|
&& attributeConverterDescriptor == null
|
||||||
|
&& getColumnSpan() == 1) {
|
||||||
|
Selectable selectable = getColumnIterator().next();
|
||||||
|
if (selectable instanceof Column) {
|
||||||
|
BasicType<?> basicType = resolution.getResolvedBasicType();
|
||||||
|
Column col = (Column) selectable;
|
||||||
|
Dialect dialect = getServiceRegistry().getService(JdbcServices.class).getDialect();
|
||||||
|
String checkConstraint = col.getCheckConstraint();
|
||||||
|
if ( checkConstraint == null && dialect.supportsColumnCheck() ) {
|
||||||
|
col.setCheckConstraint(
|
||||||
|
basicType.getJavaTypeDescriptor()
|
||||||
|
.getCheckCondition(
|
||||||
|
col.getQuotedName( dialect ),
|
||||||
|
basicType.getSqlTypeDescriptor(),
|
||||||
|
dialect
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return resolution;
|
return resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +292,13 @@ public boolean hasCheckConstraint() {
|
|||||||
return checkConstraint != null;
|
return checkConstraint != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String checkConstraint() {
|
||||||
|
if (checkConstraint==null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return " check (" + checkConstraint + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTemplate(Dialect dialect, SqmFunctionRegistry functionRegistry) {
|
public String getTemplate(Dialect dialect, SqmFunctionRegistry functionRegistry) {
|
||||||
return safeInterning(
|
return safeInterning(
|
||||||
|
@ -494,10 +494,9 @@ public Iterator sqlAlterStrings(
|
|||||||
.getColumnDefinitionUniquenessFragment( column ) );
|
.getColumnDefinitionUniquenessFragment( column ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
|
String checkConstraint = column.checkConstraint();
|
||||||
alter.append( " check(" )
|
if ( checkConstraint !=null && dialect.supportsColumnCheck() ) {
|
||||||
.append( column.getCheckConstraint() )
|
alter.append( checkConstraint );
|
||||||
.append( ")" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String columnComment = column.getComment();
|
String columnComment = column.getComment();
|
||||||
@ -578,10 +577,9 @@ public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog,
|
|||||||
.getColumnDefinitionUniquenessFragment( col ) );
|
.getColumnDefinitionUniquenessFragment( col ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
|
String checkConstraint = col.checkConstraint();
|
||||||
buf.append( " check (" )
|
if ( checkConstraint!=null && dialect.supportsColumnCheck() ) {
|
||||||
.append( col.getCheckConstraint() )
|
buf.append( checkConstraint );
|
||||||
.append( ")" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String columnComment = col.getComment();
|
String columnComment = col.getComment();
|
||||||
|
@ -26,7 +26,6 @@ public abstract class ToOne extends SimpleValue implements Fetchable {
|
|||||||
protected String referencedPropertyName;
|
protected String referencedPropertyName;
|
||||||
private String referencedEntityName;
|
private String referencedEntityName;
|
||||||
private String propertyName;
|
private String propertyName;
|
||||||
private boolean embedded;
|
|
||||||
private boolean lazy = true;
|
private boolean lazy = true;
|
||||||
protected boolean unwrapProxy;
|
protected boolean unwrapProxy;
|
||||||
protected boolean referenceToPrimaryKey = true;
|
protected boolean referenceToPrimaryKey = true;
|
||||||
@ -106,8 +105,7 @@ public boolean isSame(SimpleValue other) {
|
|||||||
public boolean isSame(ToOne other) {
|
public boolean isSame(ToOne other) {
|
||||||
return super.isSame( other )
|
return super.isSame( other )
|
||||||
&& Objects.equals( referencedPropertyName, other.referencedPropertyName )
|
&& Objects.equals( referencedPropertyName, other.referencedPropertyName )
|
||||||
&& Objects.equals( referencedEntityName, other.referencedEntityName )
|
&& Objects.equals( referencedEntityName, other.referencedEntityName );
|
||||||
&& embedded == other.embedded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValid(Mapping mapping) throws MappingException {
|
public boolean isValid(Mapping mapping) throws MappingException {
|
||||||
|
@ -60,7 +60,7 @@ public Integer toRelationalValue(E domainForm) {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getJdbcTypeCode() {
|
public int getJdbcTypeCode() {
|
||||||
return Types.INTEGER;
|
return Types.TINYINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -119,10 +119,9 @@ public String[] getSqlCreateStrings(Table table, Metadata metadata) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( col.getCheckConstraint() != null && dialect.supportsColumnCheck() ) {
|
String checkConstraint = col.checkConstraint();
|
||||||
buf.append( " check (" )
|
if ( checkConstraint != null && dialect.supportsColumnCheck() ) {
|
||||||
.append( col.getCheckConstraint() )
|
buf.append( checkConstraint );
|
||||||
.append( ")" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String columnComment = col.getComment();
|
String columnComment = col.getComment();
|
||||||
|
@ -227,7 +227,7 @@ private EnumValueConverter interpretParameters(Properties parameters) {
|
|||||||
return new OrdinalEnumValueConverter(
|
return new OrdinalEnumValueConverter(
|
||||||
enumJavaDescriptor,
|
enumJavaDescriptor,
|
||||||
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
||||||
(BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ private EnumValueConverter interpretParameters(Properties parameters) {
|
|||||||
return new OrdinalEnumValueConverter(
|
return new OrdinalEnumValueConverter(
|
||||||
enumJavaDescriptor,
|
enumJavaDescriptor,
|
||||||
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
||||||
(BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if ( isCharacterType( type ) ) {
|
else if ( isCharacterType( type ) ) {
|
||||||
@ -263,7 +263,7 @@ else if ( isCharacterType( type ) ) {
|
|||||||
return new OrdinalEnumValueConverter(
|
return new OrdinalEnumValueConverter(
|
||||||
enumJavaDescriptor,
|
enumJavaDescriptor,
|
||||||
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
||||||
(BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.java.spi.Primitive;
|
import org.hibernate.type.descriptor.java.spi.Primitive;
|
||||||
|
import org.hibernate.type.descriptor.sql.BitTypeDescriptor;
|
||||||
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||||
|
|
||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
@ -157,4 +159,11 @@ public int getDefaultSqlPrecision(Dialect dialect) {
|
|||||||
public int getDefaultSqlScale() {
|
public int getDefaultSqlScale() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCheckCondition(String columnName, SqlTypeDescriptor sqlTypeDescriptor, Dialect dialect) {
|
||||||
|
return sqlTypeDescriptor instanceof BitTypeDescriptor && !dialect.supportsBitType()
|
||||||
|
? columnName + " in (0,1)"
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,13 @@
|
|||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import javax.persistence.EnumType;
|
import javax.persistence.EnumType;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.sql.IntegerTypeDescriptor;
|
||||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
||||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
|
||||||
|
import org.hibernate.type.descriptor.sql.TinyIntTypeDescriptor;
|
||||||
|
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a Java Enum type.
|
* Describes a Java Enum type.
|
||||||
@ -32,7 +36,7 @@ public SqlTypeDescriptor getJdbcRecommendedSqlType(SqlTypeDescriptorIndicators c
|
|||||||
: context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.VARCHAR );
|
: context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.VARCHAR );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.INTEGER );
|
return context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.TINYINT );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +60,9 @@ public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
|
|||||||
else if ( Integer.class.equals( type ) ) {
|
else if ( Integer.class.equals( type ) ) {
|
||||||
return (X) toOrdinal( value );
|
return (X) toOrdinal( value );
|
||||||
}
|
}
|
||||||
|
else if ( Byte.class.equals( type ) ) {
|
||||||
|
return (X) toByte( value );
|
||||||
|
}
|
||||||
return (X) value;
|
return (X) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,10 +78,22 @@ else if ( value instanceof String ) {
|
|||||||
else if ( value instanceof Integer ) {
|
else if ( value instanceof Integer ) {
|
||||||
return fromOrdinal( (Integer) value );
|
return fromOrdinal( (Integer) value );
|
||||||
}
|
}
|
||||||
|
else if ( value instanceof Byte ) {
|
||||||
|
return fromByte( (Byte) value );
|
||||||
|
}
|
||||||
|
|
||||||
return (T) value;
|
return (T) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a value of the enum type to its ordinal value
|
||||||
|
*/
|
||||||
|
public <E extends Enum> Byte toByte(E domainForm) {
|
||||||
|
if ( domainForm == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (byte) domainForm.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a value of the enum type to its ordinal value
|
* Convert a value of the enum type to its ordinal value
|
||||||
@ -87,6 +105,17 @@ public <E extends Enum> Integer toOrdinal(E domainForm) {
|
|||||||
return domainForm.ordinal();
|
return domainForm.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpret a numeric value as the ordinal of the enum type
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <E extends Enum> E fromByte(Byte relationalForm) {
|
||||||
|
if ( relationalForm == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (E) getJavaType().getEnumConstants()[ relationalForm ];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interpret a numeric value as the ordinal of the enum type
|
* Interpret a numeric value as the ordinal of the enum type
|
||||||
*/
|
*/
|
||||||
@ -118,4 +147,24 @@ public T fromName(String relationalForm) {
|
|||||||
}
|
}
|
||||||
return (T) Enum.valueOf( getJavaType(), relationalForm.trim() );
|
return (T) Enum.valueOf( getJavaType(), relationalForm.trim() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCheckCondition(String columnName, SqlTypeDescriptor sqlTypeDescriptor, Dialect dialect) {
|
||||||
|
if (sqlTypeDescriptor instanceof TinyIntTypeDescriptor
|
||||||
|
|| sqlTypeDescriptor instanceof IntegerTypeDescriptor) {
|
||||||
|
int last = getJavaType().getEnumConstants().length - 1;
|
||||||
|
return columnName + " between 0 and " + last;
|
||||||
|
}
|
||||||
|
else if (sqlTypeDescriptor instanceof VarcharTypeDescriptor) {
|
||||||
|
StringBuilder types = new StringBuilder();
|
||||||
|
for ( Enum value : getJavaType().getEnumConstants() ) {
|
||||||
|
if (types.length() != 0) {
|
||||||
|
types.append(", ");
|
||||||
|
}
|
||||||
|
types.append("'").append( value.name() ).append("'");
|
||||||
|
}
|
||||||
|
return columnName + " in (" + types + ")";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,4 +186,17 @@ default String toString(T value) {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
Class<T> getJavaTypeClass();
|
Class<T> getJavaTypeClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 SqlTypeDescriptor} of the mapped column
|
||||||
|
* @param dialect the SQL {@link Dialect}
|
||||||
|
* @return a check constraint condition or null
|
||||||
|
*/
|
||||||
|
default String getCheckCondition(String columnName, SqlTypeDescriptor sqlType, Dialect dialect) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ public void testConvertedHqlInterpretation(SessionFactoryScope scope) {
|
|||||||
final CustomType basicType = (CustomType) selectedExpressable;
|
final CustomType basicType = (CustomType) selectedExpressable;
|
||||||
final EnumType enumType = (EnumType) basicType.getUserType();
|
final EnumType enumType = (EnumType) basicType.getUserType();
|
||||||
assertThat( enumType.getEnumValueConverter().getRelationalJavaDescriptor().getJavaType(), AssignableMatcher.assignableTo( Integer.class ) );
|
assertThat( enumType.getEnumValueConverter().getRelationalJavaDescriptor().getJavaType(), AssignableMatcher.assignableTo( Integer.class ) );
|
||||||
assertThat( enumType.sqlTypes()[0], is( Types.INTEGER ) );
|
assertThat( enumType.sqlTypes()[0], is( Types.TINYINT ) );
|
||||||
|
|
||||||
|
|
||||||
assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) );
|
assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user