From 4eb4327a54bcb721f35029f68ef9a5a2e73f9d43 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 11 Mar 2024 01:43:48 +0100 Subject: [PATCH] HHH-17825 fix npe for single-column @UniqueConstraint Note that HHH-17132 already attempted to fix the reported problem, but the fix introduced a new bug, and NPE. --- .../unique/CreateTableUniqueDelegate.java | 19 ++++-- .../MultiUniqueConstraintNameTest.java | 61 +++++++++++++++++ .../UniqueConstraintNameTest.java | 65 +++++++++++++++++++ 3 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java index ddbeb643cf..b0e893717a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/CreateTableUniqueDelegate.java @@ -80,10 +80,21 @@ public class CreateTableUniqueDelegate extends AlterTableUniqueDelegate { } private static boolean isSingleColumnUnique(Table table, UniqueKey uniqueKey) { - return uniqueKey.getColumns().size() == 1 - // Since columns are created on demand in IndexBinder.createColumn, - // we also have to check if the "real" column is unique to be safe - && ( uniqueKey.getColumn( 0 ).isUnique() || table.getColumn( uniqueKey.getColumn( 0 ) ).isUnique() ); + if ( uniqueKey.getColumns().size() == 1) { + // Since columns are created on demand in IndexBinder.createColumn, + // we also have to check if the "real" column is unique to be safe + final Column uniqueKeyColumn = uniqueKey.getColumn(0); + if ( uniqueKeyColumn.isUnique() ) { + return true; + } + else { + final Column column = table.getColumn( uniqueKeyColumn ); + return column != null && column.isUnique(); + } + } + else { + return false; + } } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java new file mode 100644 index 0000000000..078cceeeef --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/MultiUniqueConstraintNameTest.java @@ -0,0 +1,61 @@ +package org.hibernate.orm.test.schemaupdate.uniqueconstraint; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(annotatedClasses = {MultiUniqueConstraintNameTest.MyEntity.class, MultiUniqueConstraintNameTest.MyOtherEntity.class}) +public class MultiUniqueConstraintNameTest { + + @Test void test(SessionFactoryScope scope) { + scope.getSessionFactory(); + } + + @Entity + @Table(name = "my_entity", + uniqueConstraints = + @UniqueConstraint( + name = "my_other_entity_id_unique", + columnNames = {"my_other_entity_id1","my_other_entity_id2"} + ), + indexes = @Index(name = "some_long_index", columnList = "some_long")) + static class MyEntity { + + @Id + long id1; + @Id + long id2; + + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "my_other_entity_id1") + @JoinColumn(name = "my_other_entity_id2") + private MyOtherEntity myOtherEntity; + + @Column(name = "some_long") + private long someLong; + + } + + @Entity + @Table(name = "my_other_entity") + static class MyOtherEntity { + @Id + long id1; + @Id + long id2; + + @Column(name = "some_string") + private String someString; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java new file mode 100644 index 0000000000..de0e073cd4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemaupdate/uniqueconstraint/UniqueConstraintNameTest.java @@ -0,0 +1,65 @@ +package org.hibernate.orm.test.schemaupdate.uniqueconstraint; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(annotatedClasses = {UniqueConstraintNameTest.MyEntity.class, UniqueConstraintNameTest.MyOtherEntity.class}) +@JiraKey("HHH-17825") +@JiraKey("HHH-17132") +public class UniqueConstraintNameTest { + + @Test void test(SessionFactoryScope scope) { + scope.getSessionFactory(); + } + + @Entity + @Table(name = "my_entity", + uniqueConstraints = + @UniqueConstraint( + name = "my_other_entity_id_unique", + columnNames = "my_other_entity_id" + ), + indexes = @Index(name = "some_long_index", columnList = "some_long")) + static class MyEntity { + + @Id + @GeneratedValue + long id; + + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "my_other_entity_id", + updatable = false, + foreignKey = @ForeignKey(name = "FK_moe")) + private MyOtherEntity myOtherEntity; + + @Column(name = "some_long") + private long someLong; + + } + + @Entity + @Table(name = "my_other_entity") + static class MyOtherEntity { + @Id + @GeneratedValue + long id; + + @Column(name = "some_string") + private String someString; + } +}