reenable check constraings on enum columns, and use MySQL enum column types
MySQL doesn't have real check constraints, but it does have something just as good for this special case
This commit is contained in:
parent
7208bcea41
commit
baffbc0aae
|
@ -668,6 +668,10 @@ public abstract class Dialect implements ConversionContext {
|
|||
}
|
||||
}
|
||||
|
||||
public String getEnumTypeDeclaration(int sqlType, Class<? extends Enum<?>> enumClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value.
|
||||
*/
|
||||
|
@ -2268,8 +2272,8 @@ public abstract class Dialect implements ConversionContext {
|
|||
*
|
||||
* @return The appropriate empty values clause.
|
||||
*
|
||||
* @deprecated Override {@link org.hibernate.sql.ast.spi.AbstractSqlAstTranslator#renderInsertIntoNoColumns}
|
||||
* on the {@link #getSqlAstTranslatorFactory() translator} returned by this dialect
|
||||
* @deprecated Override the method {@code renderInsertIntoNoColumns()} on the
|
||||
* {@link #getSqlAstTranslatorFactory() translator} returned by this dialect
|
||||
*/
|
||||
@Deprecated( since = "6" )
|
||||
public String getNoColumnsInsertString() {
|
||||
|
|
|
@ -368,11 +368,6 @@ public class HSQLDialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsColumnCheck() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
return HSQLSequenceSupport.INSTANCE;
|
||||
|
|
|
@ -77,6 +77,8 @@ 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;
|
||||
|
@ -752,6 +754,23 @@ public class MySQLDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryHintString(String query, String hints) {
|
||||
return IndexQueryHintHandler.INSTANCE.addQueryHints( query, hints );
|
||||
|
|
|
@ -317,21 +317,26 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
throw new IllegalStateException( "Unable to resolve BasicValue : " + this );
|
||||
}
|
||||
|
||||
final Selectable column = getColumn();
|
||||
if ( column instanceof Column && resolution.getValueConverter() == null ) {
|
||||
final Column physicalColumn = (Column) column;
|
||||
if ( physicalColumn.getSqlTypeCode() == null ) {
|
||||
physicalColumn.setSqlTypeCode( resolution.getJdbcType().getDefaultSqlTypeCode() );
|
||||
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 BasicType<?> basicType = resolution.getLegacyResolvedBasicType();
|
||||
final Dialect dialect = getServiceRegistry().getService( JdbcServices.class ).getDialect();
|
||||
final String checkConstraint = physicalColumn.getCheckConstraint();
|
||||
if ( checkConstraint == null && dialect.supportsColumnCheck() ) {
|
||||
physicalColumn.setCheckConstraint(
|
||||
basicType.getJavaTypeDescriptor().getCheckCondition(
|
||||
physicalColumn.getQuotedName( dialect ),
|
||||
basicType.getJdbcType(),
|
||||
column.setSpecializedTypeDeclaration(
|
||||
resolution.getDomainJavaType().getSpecializedTypeDeclaration(
|
||||
resolution.getJdbcType(),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
if ( dialect.supportsColumnCheck() && !column.hasCheckConstraint() ) {
|
||||
column.setCheckConstraint(
|
||||
resolution.getDomainJavaType().getCheckCondition(
|
||||
column.getQuotedName(dialect),
|
||||
resolution.getJdbcType(),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
|
|
|
@ -61,6 +61,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
private String customWrite;
|
||||
private String customRead;
|
||||
private Size columnSize;
|
||||
private String specializedTypeDeclaration;
|
||||
|
||||
public Column() {
|
||||
}
|
||||
|
@ -431,6 +432,18 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
return getClass().getSimpleName() + '(' + getName() + ')';
|
||||
}
|
||||
|
||||
public void setSpecializedTypeDeclaration(String specializedTypeDeclaration) {
|
||||
this.specializedTypeDeclaration = specializedTypeDeclaration;
|
||||
}
|
||||
|
||||
public String getSpecializedTypeDeclaration() {
|
||||
return specializedTypeDeclaration;
|
||||
}
|
||||
|
||||
public boolean hasSpecializedTypeDeclaration() {
|
||||
return specializedTypeDeclaration != null;
|
||||
}
|
||||
|
||||
public String getCheckConstraint() {
|
||||
return checkConstraint;
|
||||
}
|
||||
|
@ -444,7 +457,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
}
|
||||
|
||||
public String checkConstraint() {
|
||||
if (checkConstraint==null) {
|
||||
if ( checkConstraint == null ) {
|
||||
return null;
|
||||
}
|
||||
return " check (" + checkConstraint + ")";
|
||||
|
@ -540,7 +553,6 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
|
|||
this.generatedAs = generatedAs;
|
||||
}
|
||||
|
||||
|
||||
public String getCustomWrite() {
|
||||
return customWrite;
|
||||
}
|
||||
|
|
|
@ -461,8 +461,11 @@ public class Table implements Serializable, ContributableDatabaseObject {
|
|||
dialect,
|
||||
metadata
|
||||
);
|
||||
if ( column.getGeneratedAs()==null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
alter.append( ' ' ).append( columnType );
|
||||
if ( column.hasSpecializedTypeDeclaration() ) {
|
||||
alter.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
|
||||
}
|
||||
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
alter.append(' ').append(columnType);
|
||||
}
|
||||
|
||||
final String defaultValue = column.getDefaultValue();
|
||||
|
@ -490,9 +493,8 @@ public class Table implements Serializable, ContributableDatabaseObject {
|
|||
.getColumnDefinitionUniquenessFragment( column, sqlStringGenerationContext ) );
|
||||
}
|
||||
|
||||
final String checkConstraint = column.checkConstraint();
|
||||
if ( checkConstraint !=null && dialect.supportsColumnCheck() ) {
|
||||
alter.append( checkConstraint );
|
||||
if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
|
||||
alter.append( column.checkConstraint() );
|
||||
}
|
||||
|
||||
final String columnComment = column.getComment();
|
||||
|
|
|
@ -52,7 +52,7 @@ public class StandardTableExporter implements Exporter<Table> {
|
|||
|
||||
try {
|
||||
String formattedTableName = context.format( tableName );
|
||||
StringBuilder buf =
|
||||
StringBuilder createTable =
|
||||
new StringBuilder( tableCreateString( table.hasPrimaryKey() ) )
|
||||
.append( ' ' )
|
||||
.append( formattedTableName )
|
||||
|
@ -78,96 +78,98 @@ public class StandardTableExporter implements Exporter<Table> {
|
|||
}
|
||||
|
||||
boolean isFirst = true;
|
||||
for ( Column col : table.getColumns() ) {
|
||||
for ( Column column : table.getColumns() ) {
|
||||
if ( isFirst ) {
|
||||
isFirst = false;
|
||||
}
|
||||
else {
|
||||
buf.append( ", " );
|
||||
createTable.append( ", " );
|
||||
}
|
||||
|
||||
String colName = col.getQuotedName( dialect );
|
||||
buf.append( colName );
|
||||
String colName = column.getQuotedName( dialect );
|
||||
createTable.append( colName );
|
||||
|
||||
if ( isPrimaryKeyIdentity && colName.equals( pkColName ) ) {
|
||||
// to support dialects that have their own identity data type
|
||||
if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) {
|
||||
buf.append( ' ' ).append(
|
||||
col.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata )
|
||||
createTable.append( ' ' ).append(
|
||||
column.getSqlType( metadata.getDatabase().getTypeConfiguration(), dialect, metadata )
|
||||
);
|
||||
}
|
||||
String identityColumnString = dialect.getIdentityColumnSupport()
|
||||
.getIdentityColumnString( col.getSqlTypeCode(metadata) );
|
||||
buf.append( ' ' ).append( identityColumnString );
|
||||
.getIdentityColumnString( column.getSqlTypeCode(metadata) );
|
||||
createTable.append( ' ' ).append( identityColumnString );
|
||||
}
|
||||
else {
|
||||
final String columnType = col.getSqlType(
|
||||
final String columnType = column.getSqlType(
|
||||
metadata.getDatabase().getTypeConfiguration(),
|
||||
dialect,
|
||||
metadata
|
||||
);
|
||||
if ( col.getGeneratedAs()==null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
buf.append( ' ' ).append( columnType );
|
||||
if ( column.hasSpecializedTypeDeclaration() ) {
|
||||
createTable.append( ' ' ).append( column.getSpecializedTypeDeclaration() );
|
||||
}
|
||||
else if ( column.getGeneratedAs() == null || dialect.hasDataTypeBeforeGeneratedAs() ) {
|
||||
createTable.append( ' ' ).append( columnType );
|
||||
}
|
||||
|
||||
String defaultValue = col.getDefaultValue();
|
||||
String defaultValue = column.getDefaultValue();
|
||||
if ( defaultValue != null ) {
|
||||
buf.append( " default " ).append( defaultValue );
|
||||
createTable.append( " default " ).append( defaultValue );
|
||||
}
|
||||
|
||||
String generatedAs = col.getGeneratedAs();
|
||||
String generatedAs = column.getGeneratedAs();
|
||||
if ( generatedAs != null) {
|
||||
buf.append( dialect.generatedAs( generatedAs ) );
|
||||
createTable.append( dialect.generatedAs( generatedAs ) );
|
||||
}
|
||||
|
||||
if ( col.isNullable() ) {
|
||||
buf.append( dialect.getNullColumnString( columnType ) );
|
||||
if ( column.isNullable() ) {
|
||||
createTable.append( dialect.getNullColumnString( columnType ) );
|
||||
}
|
||||
else {
|
||||
buf.append( " not null" );
|
||||
createTable.append( " not null" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( col.isUnique() && !table.isPrimaryKey( col ) ) {
|
||||
String keyName = Constraint.generateName( "UK_", table, col );
|
||||
if ( column.isUnique() && !table.isPrimaryKey( column ) ) {
|
||||
String keyName = Constraint.generateName( "UK_", table, column );
|
||||
UniqueKey uk = table.getOrCreateUniqueKey( keyName );
|
||||
uk.addColumn( col );
|
||||
buf.append(
|
||||
uk.addColumn( column );
|
||||
createTable.append(
|
||||
dialect.getUniqueDelegate()
|
||||
.getColumnDefinitionUniquenessFragment( col, context )
|
||||
.getColumnDefinitionUniquenessFragment( column, context )
|
||||
);
|
||||
}
|
||||
|
||||
String checkConstraint = col.checkConstraint();
|
||||
if ( checkConstraint != null && dialect.supportsColumnCheck() ) {
|
||||
buf.append( checkConstraint );
|
||||
if ( dialect.supportsColumnCheck() && column.hasCheckConstraint() ) {
|
||||
createTable.append( column.checkConstraint() );
|
||||
}
|
||||
|
||||
String columnComment = col.getComment();
|
||||
String columnComment = column.getComment();
|
||||
if ( columnComment != null ) {
|
||||
buf.append( dialect.getColumnComment( columnComment ) );
|
||||
createTable.append( dialect.getColumnComment( columnComment ) );
|
||||
}
|
||||
}
|
||||
if ( table.hasPrimaryKey() ) {
|
||||
buf.append( ", " )
|
||||
createTable.append( ", " )
|
||||
.append( table.getPrimaryKey().sqlConstraintString( dialect ) );
|
||||
}
|
||||
|
||||
buf.append( dialect.getUniqueDelegate().getTableCreationUniqueConstraintsFragment( table, context ) );
|
||||
createTable.append( dialect.getUniqueDelegate().getTableCreationUniqueConstraintsFragment( table, context ) );
|
||||
|
||||
applyTableCheck( table, buf );
|
||||
applyTableCheck( table, createTable );
|
||||
|
||||
buf.append( ')' );
|
||||
createTable.append( ')' );
|
||||
|
||||
if ( table.getComment() != null ) {
|
||||
buf.append( dialect.getTableComment( table.getComment() ) );
|
||||
createTable.append( dialect.getTableComment( table.getComment() ) );
|
||||
}
|
||||
|
||||
applyTableTypeString( buf );
|
||||
applyTableTypeString( createTable );
|
||||
|
||||
List<String> sqlStrings = new ArrayList<>();
|
||||
sqlStrings.add( buf.toString() );
|
||||
sqlStrings.add( createTable.toString() );
|
||||
|
||||
applyComments( table, formattedTableName, sqlStrings );
|
||||
|
||||
|
|
|
@ -220,4 +220,8 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
|
|||
return dialect.getEnumCheckCondition( columnName, jdbcType.getJdbcTypeCode(), getJavaTypeClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecializedTypeDeclaration(JdbcType jdbcType, Dialect dialect) {
|
||||
return dialect.getEnumTypeDeclaration( jdbcType.getJdbcTypeCode(), getJavaTypeClass() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,6 +289,10 @@ public interface JavaType<T> extends Serializable {
|
|||
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.
|
||||
|
|
|
@ -14,8 +14,6 @@ import org.hibernate.Session;
|
|||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.type.descriptor.JdbcBindingLogging;
|
||||
import org.hibernate.type.descriptor.JdbcExtractingLogging;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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.orm.test.schematools;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.orm.test.tool.schema.ExecutionOptionsTestImpl;
|
||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
||||
import org.hibernate.tool.schema.SourceType;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
|
||||
import org.hibernate.tool.schema.internal.exec.GenerationTarget;
|
||||
import org.hibernate.tool.schema.internal.exec.JdbcContext;
|
||||
import org.hibernate.tool.schema.spi.SchemaCreator;
|
||||
import org.hibernate.tool.schema.spi.SchemaManagementTool;
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Dialect#getFallbackSchemaManagementTool}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EnumCheckTests {
|
||||
@Test
|
||||
public void testFallbackToolIsPickedUp() {
|
||||
ServiceRegistryScope.using(
|
||||
() -> {
|
||||
return new StandardServiceRegistryBuilder()
|
||||
.applySetting( AvailableSettings.DIALECT, CustomDialect.class.getName() )
|
||||
.build();
|
||||
},
|
||||
(registryScope) -> {
|
||||
final StandardServiceRegistry registry = registryScope.getRegistry();
|
||||
final Metadata metadata = new MetadataSources( registry )
|
||||
.addAnnotatedClass( SimpleEntity.class )
|
||||
.buildMetadata();
|
||||
|
||||
final HibernateSchemaManagementTool tool = (HibernateSchemaManagementTool) registry.getService( SchemaManagementTool.class );
|
||||
final Map<String, Object> settings = registry.getService( ConfigurationService.class ).getSettings();
|
||||
final SchemaCreator schemaCreator = tool.getSchemaCreator( settings );
|
||||
schemaCreator.doCreation(
|
||||
metadata,
|
||||
new ExecutionOptionsTestImpl(),
|
||||
contributed -> true,
|
||||
new SourceDescriptor() {
|
||||
@Override
|
||||
public SourceType getSourceType() {
|
||||
return SourceType.METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptSourceInput getScriptSourceInput() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
new TargetDescriptor() {
|
||||
@Override
|
||||
public EnumSet<TargetType> getTargetTypes() {
|
||||
return EnumSet.of( TargetType.DATABASE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptTargetOutput getScriptTargetOutput() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assertThat( CollectingGenerationTarget.commands.get(0).contains( "check ('SOURCE','CLASS','RUNTIME')") );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static class CollectingGenerationTarget implements GenerationTarget {
|
||||
public static final List<String> commands = new ArrayList<>();
|
||||
|
||||
public CollectingGenerationTarget(JdbcContext jdbcContext, boolean needsAutoCommit) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
commands.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(String command) {
|
||||
commands.add( command );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class SchemaManagementToolImpl extends HibernateSchemaManagementTool {
|
||||
@Override
|
||||
protected GenerationTarget buildDatabaseTarget(JdbcContext jdbcContext, boolean needsAutoCommit) {
|
||||
return new CollectingGenerationTarget( jdbcContext, needsAutoCommit );
|
||||
}
|
||||
}
|
||||
|
||||
public static class CustomDialect extends Dialect {
|
||||
@Override
|
||||
public SchemaManagementTool getFallbackSchemaManagementTool(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
|
||||
return new SchemaManagementToolImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "SimpleEntity" )
|
||||
@Table( name = "SimpleEntity" )
|
||||
public static class SimpleEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
@Basic
|
||||
private String name;
|
||||
@Enumerated(EnumType.STRING)
|
||||
RetentionPolicy retentionPolicy;
|
||||
|
||||
private SimpleEntity() {
|
||||
// for use by Hibernate
|
||||
}
|
||||
|
||||
public SimpleEntity(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue