[JIRA] (HHH-16839) Failing to update a one-to-one lazy association with `OptimisticLockType.DIRTY` and enabled bytecode enhancement
by Marko Bekhta (JIRA)
Marko Bekhta ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiMDAzNzU3YTk0... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16839?atlOrigin=eyJpIjoiMDAzNz... ) HHH-16839 ( https://hibernate.atlassian.net/browse/HHH-16839?atlOrigin=eyJpIjoiMDAzNz... ) Failing to update a one-to-one lazy association with `OptimisticLockType.DIRTY` and enabled bytecode enhancement ( https://hibernate.atlassian.net/browse/HHH-16839?atlOrigin=eyJpIjoiMDAzNz... )
Change By: Marko Bekhta ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
When an update is performed on a lazy one-to-one association with optimistic locking set to {{OptimisticLockType.DIRTY}} previous state of that association is not loaded, leading to missing parameters required to perform an update statement.
With a model like:
{code:java}@Entity(name = "Person")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public static class Person {
@Id
Long id;
@Column
String name;
// NOTE: issue is only reproducable with lazy fetching
// and enabled bytecode enhancement
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Address address;
}
@Entity(name = "Address")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public static class Address {
@Id
Long id;
@Column
String street;
}{code}
Then an operation that leads to an error:
{code:java}// get a person that has a lazy address
Person person = entityManager.find( Person.class, 1L );
// set a new value to a lazy association:
person.getAddress().setStreet( "new Street" );{code}
Results in:
{noformat}# Query executed:
Hibernate:
update
Address
set
street=?
where
id=?
and street=?
binding parameter (1:VARCHAR) <- [new Street]
binding parameter (2:BIGINT) <- [10]
SQL Error: 90012, SQLState: 90012
Parameter "#3" is not set; SQL statement:
update Address set street=? where id=? and street=? [90012-214]
# Error stack trace:
Caused by: org.hibernate.exception.DataException: could not execute statement [Parameter "#3" is not set; SQL statement:
update Address set street=? where id=? and street=? [90012-214]] [update Address set street=? where id=? and street=?]
at app//org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:53)
at app//org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)
at app//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:108)
at app//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:278)
at app//org.hibernate.engine.jdbc.mutation.internal.AbstractMutationExecutor.performNonBatchedMutation(AbstractMutationExecutor.java:108)
at app//org.hibernate.engine.jdbc.mutation.internal.MutationExecutorSingleNonBatched.performNonBatchedOperations(MutationExecutorSingleNonBatched.java:40)
at app//org.hibernate.engine.jdbc.mutation.internal.AbstractMutationExecutor.execute(AbstractMutationExecutor.java:53)
at app//org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.doDynamicUpdate(UpdateCoordinatorStandard.java:947)
at app//org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.performUpdate(UpdateCoordinatorStandard.java:287)
at app//org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.coordinateUpdate(UpdateCoordinatorStandard.java:217)
at app//org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2731)
at app//org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:166)
at app//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:633)
at app//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:502)
at app//org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:363)
at app//org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at app//org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
at app//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1414)
at app//org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:496)
at app//org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2324)
at app//org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1987)
at app//org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439)
at app//org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:169)
at app//org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:267)
at app//org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101){noformat}
Will submit a A reproducer : [https://github. com/hibernate/hibernate-orm/pull/6862|https://github.com/hibernate/hibern...]
( https://hibernate.atlassian.net/browse/HHH-16839#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16839#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100227- sha1:32df33b )
1 year, 6 months
[JIRA] (HHH-16839) Failing to update a one-to-one lazy association with `OptimisticLockType.DIRTY` and enabled bytecode enhancement
by Marko Bekhta (JIRA)
Marko Bekhta ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *created* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiOWExMWFkN2Nj... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16839?atlOrigin=eyJpIjoiOWExMW... ) HHH-16839 ( https://hibernate.atlassian.net/browse/HHH-16839?atlOrigin=eyJpIjoiOWExMW... ) Failing to update a one-to-one lazy association with `OptimisticLockType.DIRTY` and enabled bytecode enhancement ( https://hibernate.atlassian.net/browse/HHH-16839?atlOrigin=eyJpIjoiOWExMW... )
Issue Type: Bug Affects Versions: 6.2.5 Assignee: Unassigned Components: hibernate-core Created: 23/Jun/2023 08:27 AM Priority: Major Reporter: Marko Bekhta ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
When an update is performed on a lazy one-to-one association with optimistic locking set to OptimisticLockType.DIRTY previous state of that association is not loaded, leading to missing parameters required to perform an update statement.
With a model like:
@Entity(name = "Person" )
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public static class Person {
@Id
Long id;
@Column
String name;
// NOTE: issue is only reproducable with lazy fetching
// and enabled bytecode enhancement
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Address address;
}
@Entity(name = "Address" )
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
public static class Address {
@Id
Long id;
@Column
String street;
}
Then an operation that leads to an error:
// get a person that has a lazy address
Person person = entityManager.find( Person.class, 1L );
// set a new value to a lazy association:
person.getAddress().setStreet( " new Street" );
Results in:
# Query executed:
Hibernate:
update
Address
set
street=?
where
id=?
and street=?
binding parameter (1:VARCHAR) <- [new Street]
binding parameter (2:BIGINT) <- [10]
SQL Error: 90012, SQLState: 90012
Parameter "#3" is not set; SQL statement:
update Address set street=? where id=? and street=? [90012-214]
# Error stack trace:
Caused by: org.hibernate.exception.DataException: could not execute statement [Parameter "#3" is not set; SQL statement:
update Address set street=? where id=? and street=? [90012-214]] [update Address set street=? where id=? and street=?]
at app//org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:53)
at app//org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)
at app//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:108)
at app//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:278)
at app//org.hibernate.engine.jdbc.mutation.internal.AbstractMutationExecutor.performNonBatchedMutation(AbstractMutationExecutor.java:108)
at app//org.hibernate.engine.jdbc.mutation.internal.MutationExecutorSingleNonBatched.performNonBatchedOperations(MutationExecutorSingleNonBatched.java:40)
at app//org.hibernate.engine.jdbc.mutation.internal.AbstractMutationExecutor.execute(AbstractMutationExecutor.java:53)
at app//org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.doDynamicUpdate(UpdateCoordinatorStandard.java:947)
at app//org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.performUpdate(UpdateCoordinatorStandard.java:287)
at app//org.hibernate.persister.entity.mutation.UpdateCoordinatorStandard.coordinateUpdate(UpdateCoordinatorStandard.java:217)
at app//org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2731)
at app//org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:166)
at app//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:633)
at app//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:502)
at app//org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:363)
at app//org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at app//org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
at app//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1414)
at app//org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:496)
at app//org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2324)
at app//org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1987)
at app//org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439)
at app//org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:169)
at app//org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:267)
at app//org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
Will submit a reproducer.
( https://hibernate.atlassian.net/browse/HHH-16839#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16839#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100227- sha1:32df33b )
1 year, 6 months
[JIRA] (HHH-16838) Cannot use columnDefinition CHAR overriding for enumerated column on MySQL
by Hyeonmin Park (JIRA)
Hyeonmin Park ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiNjhhMzJkZmI5... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16838?atlOrigin=eyJpIjoiNjhhMz... ) HHH-16838 ( https://hibernate.atlassian.net/browse/HHH-16838?atlOrigin=eyJpIjoiNjhhMz... ) Cannot use columnDefinition CHAR overriding for enumerated column on MySQL ( https://hibernate.atlassian.net/browse/HHH-16838?atlOrigin=eyJpIjoiNjhhMz... )
Change By: Hyeonmin Park ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
The fix for [HHH-16498]([https://github.com/hibernate/hibernate-orm/pull/6539|https://... seems to be making {{columnDefinition}} value ignored.
h2. Reproducer
Mostly copied from { {MySqlExistingEnumColumnValidationTest}}:
{ code:java}package org.hibernate.orm.test.schemavalidation;
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 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.MySQLDialect;
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.tool.hbm2ddl.SchemaValidator;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static jakarta.persistence.GenerationType.IDENTITY;
@JiraKey("HHH-16838")
@RequiresDialect(MySQLDialect.class)
public class MySqlExistingCharAsJavaEnumColumnValidationTest 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_as_varchar varchar(20)"
+ ", sign_position_as_char char(20)"
+ ", PRIMARY KEY (id))" )
.executeUpdate();
} );
}
@After
public void tearDown() {
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.createNativeQuery( "DROP TABLE en CASCADE" ).executeUpdate();
} );
}
@Test
public void testSynonymUsingGroupedSchemaValidator() {
ssr = new StandardServiceRegistryBuilder()
.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_as_varchar")
private SignPosition signPositionAsVarchar;
@Enumerated(EnumType.STRING)
@Column(name = "sign_position_as_char", columnDefinition = "char(20)")
private SignPosition signPositionAsChar;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public SignPosition getSignPositionAsVarchar() {
return signPositionAsVarchar;
}
public void setSignPositionAsVarchar(SignPosition signPositionAsVarchar) {
this.signPositionAsVarchar = signPositionAsVarchar;
}
public SignPosition getSignPositionAsChar() {
return signPositionAsChar;
}
public void setSignPositionAsChar(SignPosition signPositionAsChar) {
this.signPositionAsChar = signPositionAsChar;
}
}
public enum SignPosition {
AFTER_NO_SPACE, AFTER_WITH_SPACE, BEFORE_NO_SPACE, BEFORE_WITH_SPACE
}
}{code}
h2. Expected result
Since {{signPositionAsChar}} is both declared as char(20) in the SQL query and the {{columnDefinition}}, the test should be passed.
h2. Actual result
{quote}Schema-validation: wrong column type encountered in column [sign_position_as_char] in table [en]; found [char (Types#CHAR)], but expecting [char(20) (Types#VARCHAR)]{quote}
h2. Comments
* {{ColumnDefinitions.getSqlType(Column, Metadata)}} returns {{enum ('AFTER_NO_SPACE','AFTER_WITH_SPACE','BEFORE_NO_SPACE','BEFORE_WITH_SPACE')}} and {{column.getSqlType( metadata )}} properly returns {{char(20)}}. I think there should exist a check for the pre-configured {{sqlType}}.
* The message of {{SchemaManagementException}} is not matched to {{ColumnDefinitions.getSqlType(Column, Metadata)}} but since it’s a private method, {{AbstractSchemaValidator.validateColumnType}} cannot know what’s going on exactly.
* Maybe it’s related to [https://hibernate.atlassian.net/browse/HHH-16670|https://hibernate.atlass...] or [https://hibernate.atlassian.net/browse/HHH-16694|https://hibernate.atlass...] but they say that those are occurring prior to 6.2.5.
I’ve confirmed this issue is not affected until 6.2.2 (via Spring Boot 3.1.0), also 6.2.3 nor 6.2.4 (checked manually by changing {{ext['hibernate.version']}}) so it can be not related, though.
h2. Naive solution
Didn’t run the full test, but confirmed fixes this issue.
{noformat} .../hibernate/boot/model/internal/AnnotatedColumn.java | 1 +
.../src/main/java/org/hibernate/mapping/Column.java | 9 +++++++++
.../tool/schema/internal/ColumnDefinitions.java | 3 +++
3 files changed, 13 insertions(+)
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java
index 02beed2fff..1eaca964df 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java
@@ -294,6 +294,7 @@ public class AnnotatedColumn {
mappingColumn.setResolvedCustomRead( readExpression );
mappingColumn.setCustomWrite( writeExpression );
+ mappingColumn.setColumnDefinitionConfigured( sqlType != null );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Column.java b/hibernate-core/src/main/java/org/hibernate/mapping/Column.java
index dde3b634d3..88debbe606 100644
--- a/hibernate-core/src/main/java/org/hibernate/mapping/Column.java
+++ b/hibernate-core/src/main/java/org/hibernate/mapping/Column.java
@@ -69,6 +69,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
private Size columnSize;
private String specializedTypeDeclaration;
private java.util.List<CheckConstraint> checkConstraints = new ArrayList<>();
+ private boolean columnDefinitionConfigured;
public Column() {
}
@@ -502,6 +503,14 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
return unmodifiableList( checkConstraints );
}
+ public void setColumnDefinitionConfigured(boolean columnDefinitionConfigured) {
+ this.columnDefinitionConfigured = columnDefinitionConfigured;
+ }
+
+ public boolean isColumnDefinitionConfigured() {
+ return columnDefinitionConfigured;
+ }
+
@Deprecated(since = "6.2")
public String getCheckConstraint() {
if ( checkConstraints.isEmpty() ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java
index 16dbb60b32..3ad4912a42 100644
--- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java
+++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java
@@ -44,6 +44,9 @@ class ColumnDefinitions {
}
private static String getSqlType(Column column, Metadata metadata) {
+ if ( column.isColumnDefinitionConfigured() ) {
+ return column.getSqlType( metadata );
+ }
if ( column.hasSpecializedTypeDeclaration() ) {
return column.getSpecializedTypeDeclaration();
}{noformat}
( https://hibernate.atlassian.net/browse/HHH-16838#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16838#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100227- sha1:d138f7b )
1 year, 6 months
[JIRA] (HHH-16838) Cannot use columnDefinition CHAR overriding for enumerated column on MySQL
by Hyeonmin Park (JIRA)
Hyeonmin Park ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiNWE3NWQ2ZTYx... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16838?atlOrigin=eyJpIjoiNWE3NW... ) HHH-16838 ( https://hibernate.atlassian.net/browse/HHH-16838?atlOrigin=eyJpIjoiNWE3NW... ) Cannot use columnDefinition CHAR overriding for enumerated column on MySQL ( https://hibernate.atlassian.net/browse/HHH-16838?atlOrigin=eyJpIjoiNWE3NW... )
Change By: Hyeonmin Park ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
The fix for [HHH-16498]([https://github.com/hibernate/hibernate-orm/pull/6539|https://... seems to be making {{columnDefinition}} value ignored.
h2. Reproducer
{code:java}package org.hibernate.orm.test.schemavalidation;
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 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.MySQLDialect;
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.tool.hbm2ddl.SchemaValidator;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static jakarta.persistence.GenerationType.IDENTITY;
@JiraKey("HHH-16838")
@RequiresDialect(MySQLDialect.class)
public class MySqlExistingCharAsJavaEnumColumnValidationTest 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_as_varchar varchar(20)"
+ ", sign_position_as_char char(20)"
+ ", PRIMARY KEY (id))" )
.executeUpdate();
} );
}
@After
public void tearDown() {
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.createNativeQuery( "DROP TABLE en CASCADE" ).executeUpdate();
} );
}
@Test
public void testSynonymUsingGroupedSchemaValidator() {
ssr = new StandardServiceRegistryBuilder()
.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_as_varchar")
private SignPosition signPositionAsVarchar;
@Enumerated(EnumType.STRING)
@Column(name = "sign_position_as_char", columnDefinition = "char(20)")
private SignPosition signPositionAsChar;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public SignPosition getSignPositionAsVarchar() {
return signPositionAsVarchar;
}
public void setSignPositionAsVarchar(SignPosition signPositionAsVarchar) {
this.signPositionAsVarchar = signPositionAsVarchar;
}
public SignPosition getSignPositionAsChar() {
return signPositionAsChar;
}
public void setSignPositionAsChar(SignPosition signPositionAsChar) {
this.signPositionAsChar = signPositionAsChar;
}
}
public enum SignPosition {
AFTER_NO_SPACE, AFTER_WITH_SPACE, BEFORE_NO_SPACE, BEFORE_WITH_SPACE
}
}{code}
h2. Expected result
Since {{signPositionAsChar}} is both declared as char(20) in the SQL query and the {{columnDefinition}}, the test should be passed.
h2. Actual result
{quote}Schema-validation: wrong column type encountered in column [sign_position_as_char] in table [en]; found [char (Types#CHAR)], but expecting [char(20) (Types#VARCHAR)]{quote}
h2. Comments
* {{ColumnDefinitions.getSqlType(Column, Metadata)}} returns {{enum ('AFTER_NO_SPACE','AFTER_WITH_SPACE','BEFORE_NO_SPACE','BEFORE_WITH_SPACE')}} and {{column.getSqlType( metadata )}} properly returns {{char(20)}}. I think there should exist a check for the pre-configured {{sqlType}}.
* The message of {{SchemaManagementException}} is not matched to {{ColumnDefinitions.getSqlType(Column, Metadata)}} but since it’s a private method, {{AbstractSchemaValidator.validateColumnType}} cannot know what’s going on exactly.
* Maybe it’s related to [https://hibernate.atlassian.net/browse/HHH-16670|https://hibernate.atlass...] or [https://hibernate.atlassian.net/browse/HHH-16694|https://hibernate.atlass...] but they say that those are occurring prior to 6.2.5.
I’ve confirmed this issue is not affected until 6.2.2 (via Spring Boot 3.1.0), also 6.2.3 nor 6.2.4 (checked manually by changing {{ext['hibernate.version']}}) so it can be not related, though.
h2. Naive solution
Didn’t run the full test, but confirmed fixes this issue.
{noformat} .../hibernate/boot/model/internal/AnnotatedColumn.java | 1 +
.../src/main/java/org/hibernate/mapping/Column.java | 9 +++++++++
.../tool/schema/internal/ColumnDefinitions.java | 3 +++
3 files changed, 13 insertions(+)
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java
index 02beed2fff..1eaca964df 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java
@@ -294,6 +294,7 @@ public class AnnotatedColumn {
mappingColumn.setResolvedCustomRead( readExpression );
mappingColumn.setCustomWrite( writeExpression );
+ mappingColumn.setColumnDefinitionConfigured( sqlType != null );
}
}
diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Column.java b/hibernate-core/src/main/java/org/hibernate/mapping/Column.java
index dde3b634d3..88debbe606 100644
--- a/hibernate-core/src/main/java/org/hibernate/mapping/Column.java
+++ b/hibernate-core/src/main/java/org/hibernate/mapping/Column.java
@@ -69,6 +69,7 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
private Size columnSize;
private String specializedTypeDeclaration;
private java.util.List<CheckConstraint> checkConstraints = new ArrayList<>();
+ private boolean columnDefinitionConfigured;
public Column() {
}
@@ -502,6 +503,14 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
return unmodifiableList( checkConstraints );
}
+ public void setColumnDefinitionConfigured(boolean columnDefinitionConfigured) {
+ this.columnDefinitionConfigured = columnDefinitionConfigured;
+ }
+
+ public boolean isColumnDefinitionConfigured() {
+ return columnDefinitionConfigured;
+ }
+
@Deprecated(since = "6.2")
public String getCheckConstraint() {
if ( checkConstraints.isEmpty() ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java
index 16dbb60b32..3ad4912a42 100644
--- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java
+++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java
@@ -44,6 +44,9 @@ class ColumnDefinitions {
}
private static String getSqlType(Column column, Metadata metadata) {
+ if ( column.isColumnDefinitionConfigured() ) {
+ return column.getSqlType( metadata );
+ }
if ( column.hasSpecializedTypeDeclaration() ) {
return column.getSpecializedTypeDeclaration();
}{noformat}
( https://hibernate.atlassian.net/browse/HHH-16838#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16838#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100227- sha1:d138f7b )
1 year, 6 months