mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-09 04:34:49 +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.Supplier;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
@ -26,6 +25,8 @@
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||
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.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
|
||||
@ -72,7 +73,6 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
|
||||
private boolean isNationalized;
|
||||
private boolean isLob;
|
||||
private EnumType enumerationStyle;
|
||||
private TemporalType temporalPrecision;
|
||||
|
||||
private ConverterDescriptor attributeConverterDescriptor;
|
||||
|
||||
@ -191,9 +191,7 @@ public Resolution<?> resolve() {
|
||||
() -> {
|
||||
if ( resolvedJavaClass != null ) {
|
||||
//noinspection unchecked
|
||||
return getBuildingContext().getBootstrapContext()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
return typeConfiguration.getJavaTypeDescriptorRegistry()
|
||||
.resolveDescriptor( resolvedJavaClass );
|
||||
}
|
||||
else if ( ownerName != null && propertyName != null ) {
|
||||
@ -205,8 +203,7 @@ else if ( ownerName != null && propertyName != null ) {
|
||||
|
||||
// First resolve from the BasicTypeRegistry.
|
||||
// If it does resolve, we can use the JTD instead of delegating to the JTD Regsitry.
|
||||
final BasicType basicType = getBuildingContext().getBootstrapContext()
|
||||
.getTypeConfiguration()
|
||||
final BasicType basicType = typeConfiguration
|
||||
.getBasicTypeRegistry()
|
||||
.getRegisteredType( reflectedJavaType.getName() );
|
||||
if ( basicType != null ) {
|
||||
@ -214,8 +211,7 @@ else if ( ownerName != null && propertyName != null ) {
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return getBuildingContext().getBootstrapContext()
|
||||
.getTypeConfiguration()
|
||||
return typeConfiguration
|
||||
.getJavaTypeDescriptorRegistry()
|
||||
.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;
|
||||
}
|
||||
|
||||
|
@ -292,6 +292,13 @@ public boolean hasCheckConstraint() {
|
||||
return checkConstraint != null;
|
||||
}
|
||||
|
||||
public String checkConstraint() {
|
||||
if (checkConstraint==null) {
|
||||
return null;
|
||||
}
|
||||
return " check (" + checkConstraint + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplate(Dialect dialect, SqmFunctionRegistry functionRegistry) {
|
||||
return safeInterning(
|
||||
|
@ -494,10 +494,9 @@ public Iterator sqlAlterStrings(
|
||||
.getColumnDefinitionUniquenessFragment( column ) );
|
||||
}
|
||||
|
||||
if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
|
||||
alter.append( " check(" )
|
||||
.append( column.getCheckConstraint() )
|
||||
.append( ")" );
|
||||
String checkConstraint = column.checkConstraint();
|
||||
if ( checkConstraint !=null && dialect.supportsColumnCheck() ) {
|
||||
alter.append( checkConstraint );
|
||||
}
|
||||
|
||||
String columnComment = column.getComment();
|
||||
@ -578,10 +577,9 @@ public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog,
|
||||
.getColumnDefinitionUniquenessFragment( col ) );
|
||||
}
|
||||
|
||||
if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
|
||||
buf.append( " check (" )
|
||||
.append( col.getCheckConstraint() )
|
||||
.append( ")" );
|
||||
String checkConstraint = col.checkConstraint();
|
||||
if ( checkConstraint!=null && dialect.supportsColumnCheck() ) {
|
||||
buf.append( checkConstraint );
|
||||
}
|
||||
|
||||
String columnComment = col.getComment();
|
||||
|
@ -26,7 +26,6 @@ public abstract class ToOne extends SimpleValue implements Fetchable {
|
||||
protected String referencedPropertyName;
|
||||
private String referencedEntityName;
|
||||
private String propertyName;
|
||||
private boolean embedded;
|
||||
private boolean lazy = true;
|
||||
protected boolean unwrapProxy;
|
||||
protected boolean referenceToPrimaryKey = true;
|
||||
@ -106,8 +105,7 @@ public boolean isSame(SimpleValue other) {
|
||||
public boolean isSame(ToOne other) {
|
||||
return super.isSame( other )
|
||||
&& Objects.equals( referencedPropertyName, other.referencedPropertyName )
|
||||
&& Objects.equals( referencedEntityName, other.referencedEntityName )
|
||||
&& embedded == other.embedded;
|
||||
&& Objects.equals( referencedEntityName, other.referencedEntityName );
|
||||
}
|
||||
|
||||
public boolean isValid(Mapping mapping) throws MappingException {
|
||||
|
@ -60,7 +60,7 @@ public Integer toRelationalValue(E domainForm) {
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return Types.INTEGER;
|
||||
return Types.TINYINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -119,10 +119,9 @@ public String[] getSqlCreateStrings(Table table, Metadata metadata) {
|
||||
);
|
||||
}
|
||||
|
||||
if ( col.getCheckConstraint() != null && dialect.supportsColumnCheck() ) {
|
||||
buf.append( " check (" )
|
||||
.append( col.getCheckConstraint() )
|
||||
.append( ")" );
|
||||
String checkConstraint = col.checkConstraint();
|
||||
if ( checkConstraint != null && dialect.supportsColumnCheck() ) {
|
||||
buf.append( checkConstraint );
|
||||
}
|
||||
|
||||
String columnComment = col.getComment();
|
||||
|
@ -227,7 +227,7 @@ private EnumValueConverter interpretParameters(Properties parameters) {
|
||||
return new OrdinalEnumValueConverter(
|
||||
enumJavaDescriptor,
|
||||
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(
|
||||
enumJavaDescriptor,
|
||||
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
|
||||
(BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
||||
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class )
|
||||
);
|
||||
}
|
||||
else if ( isCharacterType( type ) ) {
|
||||
@ -263,7 +263,7 @@ else if ( isCharacterType( type ) ) {
|
||||
return new OrdinalEnumValueConverter(
|
||||
enumJavaDescriptor,
|
||||
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.type.descriptor.WrapperOptions;
|
||||
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.TRUE;
|
||||
@ -157,4 +159,11 @@ public int getDefaultSqlPrecision(Dialect dialect) {
|
||||
public int getDefaultSqlScale() {
|
||||
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 javax.persistence.EnumType;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
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.SqlTypeDescriptorIndicators;
|
||||
import org.hibernate.type.descriptor.sql.TinyIntTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
|
||||
|
||||
/**
|
||||
* Describes a Java Enum type.
|
||||
@ -32,7 +36,7 @@ public SqlTypeDescriptor getJdbcRecommendedSqlType(SqlTypeDescriptorIndicators c
|
||||
: context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.VARCHAR );
|
||||
}
|
||||
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 ) ) {
|
||||
return (X) toOrdinal( value );
|
||||
}
|
||||
|
||||
else if ( Byte.class.equals( type ) ) {
|
||||
return (X) toByte( value );
|
||||
}
|
||||
return (X) value;
|
||||
}
|
||||
|
||||
@ -72,10 +78,22 @@ else if ( value instanceof String ) {
|
||||
else if ( value instanceof Integer ) {
|
||||
return fromOrdinal( (Integer) value );
|
||||
}
|
||||
else if ( value instanceof Byte ) {
|
||||
return fromByte( (Byte) 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
|
||||
@ -87,6 +105,17 @@ public <E extends Enum> Integer toOrdinal(E domainForm) {
|
||||
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
|
||||
*/
|
||||
@ -118,4 +147,24 @@ public T fromName(String relationalForm) {
|
||||
}
|
||||
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
|
||||
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 EnumType enumType = (EnumType) basicType.getUserType();
|
||||
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 ) );
|
||||
|
Loading…
x
Reference in New Issue
Block a user