From f7805e952bb05dac589d3c4cec41327009e44943 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 28 Mar 2024 16:00:40 +0100 Subject: [PATCH] HHH-17908 Allow varchar for STRING enums on MySQL in schema validation --- .../java/org/hibernate/dialect/Dialect.java | 4 + ...istingVarcharEnumColumnValidationTest.java | 112 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/ExistingVarcharEnumColumnValidationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index e38406e7b4..2a83ca6111 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -236,6 +236,7 @@ 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.isEnumType; import static org.hibernate.type.SqlTypes.isFloatOrRealOrDouble; import static org.hibernate.type.SqlTypes.isNumericOrDecimal; import static org.hibernate.type.SqlTypes.isVarbinaryType; @@ -1609,6 +1610,9 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun || isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2) || isVarcharType(typeCode1) && isVarcharType(typeCode2) || isVarbinaryType(typeCode1) && isVarbinaryType(typeCode2) + // HHH-17908: Since the runtime can cope with enum on the DDL side, + // but varchar on the ORM expectation side, let's treat the types as equivalent + || isEnumType( typeCode1 ) && isVarcharType( typeCode2 ) || sameColumnType(typeCode1, typeCode2); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/ExistingVarcharEnumColumnValidationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/ExistingVarcharEnumColumnValidationTest.java new file mode 100644 index 0000000000..5dd860767e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemavalidation/ExistingVarcharEnumColumnValidationTest.java @@ -0,0 +1,112 @@ +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.dialect.MySQLDialect; +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-17908") +@RequiresDialect( H2Dialect.class ) +@RequiresDialect( MySQLDialect.class ) +public class ExistingVarcharEnumColumnValidationTest extends BaseCoreFunctionalTestCase { + + private StandardServiceRegistry ssr; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EntityE.class }; + } + + @Before + public void setUp() { + try { + tearDown(); + } + catch (Exception ex) { + // ignore + } + TransactionUtil.doInHibernate( this::sessionFactory, session -> { + session.createNativeQuery( + "create table en (id integer not null, sign_position varchar(255) check (sign_position in ('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 + @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 + } +}