diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index d10b46d769..d531b2e844 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -73,6 +73,7 @@ import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType; import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; +import org.hibernate.type.descriptor.sql.internal.NativeEnumDdlTypeImpl; import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; @@ -106,6 +107,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithN * A {@linkplain Dialect SQL dialect} for H2. * * @author Thomas Mueller + * @author Jürgen Kreitler */ public class H2Dialect extends Dialect { private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 2, 1, 214 ); @@ -222,6 +224,7 @@ public class H2Dialect extends Dialect { ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) ); ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) ); ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) ); + ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this) ); } @Override @@ -236,6 +239,7 @@ public class H2Dialect extends Dialect { jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptor( new MySQLEnumJdbcType() ); } @Override @@ -894,6 +898,18 @@ public class H2Dialect extends Dialect { return "set referential_integrity true"; } + @Override + public String getEnumTypeDeclaration(String name, 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 getDisableConstraintsStatement() { return "set referential_integrity false"; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/H2ExistingEnumColumnValidationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/H2ExistingEnumColumnValidationTest.java new file mode 100644 index 0000000000..740e925e7d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/H2ExistingEnumColumnValidationTest.java @@ -0,0 +1,106 @@ +package org.hibernate.orm.test.schemavalidation; + +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.H2Dialect; +import org.hibernate.tool.hbm2ddl.SchemaValidator; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.transaction.TransactionUtil; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static jakarta.persistence.GenerationType.IDENTITY; + +@JiraKey("HHH-17675") +@RequiresDialect(H2Dialect.class) +public class H2ExistingEnumColumnValidationTest extends BaseCoreFunctionalTestCase { + + private StandardServiceRegistry ssr; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EntityE.class }; + } + + @Before + public void setUp() { + TransactionUtil.doInHibernate( this::sessionFactory, session -> { + session.createNativeQuery( "DROP TABLE IF EXISTS en CASCADE" ).executeUpdate(); + session.createNativeQuery( + "CREATE TABLE en (id INTEGER NOT NULL AUTO_INCREMENT, sign_position enum ('AFTER_NO_SPACE','AFTER_WITH_SPACE','BEFORE_NO_SPACE','BEFORE_WITH_SPACE'), PRIMARY KEY (id))" ) + .executeUpdate(); + } ); + } + + @After + public void tearDown() { + TransactionUtil.doInHibernate( this::sessionFactory, session -> { + session.createNativeQuery( "DROP TABLE en CASCADE" ).executeUpdate(); + } ); + } + + @Test + public void testEnumDataTypeSchemaValidator() { + ssr = ServiceRegistryUtil.serviceRegistryBuilder() + .applySetting( AvailableSettings.HBM2DDL_AUTO, "validate" ) + .build(); + try { + final MetadataSources metadataSources = new MetadataSources( ssr ); + metadataSources.addAnnotatedClass( EntityE.class ); + + new SchemaValidator().validate( metadataSources.buildMetadata() ); + } + finally { + StandardServiceRegistryBuilder.destroy( ssr ); + } + } + + + @Entity(name = "en") + @Table(name = "en") + public static class EntityE { + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id", nullable = false, updatable = false) + private Integer id; + + @Enumerated(EnumType.STRING) + @Column(name = "sign_position") + private SignPosition signPosition; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public SignPosition getSignPosition() { + return signPosition; + } + + public void setSignPosition(SignPosition signPosition) { + this.signPosition = signPosition; + } + } + + public enum SignPosition { + AFTER_NO_SPACE, AFTER_WITH_SPACE, BEFORE_NO_SPACE, BEFORE_WITH_SPACE + } +}