diff --git a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java index 74ca3bb7fc..030d376df7 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java @@ -108,6 +108,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera private Identifier qualifiedValueColumnName; private String tableName; + private Table table; private String pkColumnName; private String valueColumnName; @@ -129,6 +130,15 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera return tableName; } + /** + * The bound Table for this generator. + * + * @return The table. + */ + public final Table getTable() { + return table; + } + public synchronized Serializable generate(final SessionImplementor session, Object obj) { final WorkExecutorVisitable work = new AbstractReturningWork() { @Override @@ -279,7 +289,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera final Dialect dialect = database.getJdbcEnvironment().getDialect(); final Schema schema = database.getSchemaFor( qualifiedTableName ); - final Table table = schema.createTable( qualifiedTableName.getName(), qualifiedTableName.getName() ); + table = schema.createTable( qualifiedTableName.getName(), qualifiedTableName.getName() ); final Column pkColumn = table.createColumn( qualifiedPkColumnName ); table.getPrimaryKey().addColumn( pkColumn ); diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java index 69d3f8f9f8..a0201b66aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java @@ -33,8 +33,6 @@ import java.util.Collections; import java.util.Map; import java.util.Properties; -import org.jboss.logging.Logger; - import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -58,7 +56,9 @@ import org.hibernate.metamodel.spi.relational.Column; import org.hibernate.metamodel.spi.relational.Database; import org.hibernate.metamodel.spi.relational.Identifier; import org.hibernate.metamodel.spi.relational.ObjectName; +import org.hibernate.metamodel.spi.relational.Table; import org.hibernate.type.Type; +import org.jboss.logging.Logger; /** * An enhanced version of table-based id generation. @@ -170,6 +170,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab private Identifier qualifiedValueColumnName; private String tableName; + private Table table; private String segmentColumnName; private String segmentValue; @@ -209,6 +210,15 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab return tableName; } + /** + * The bound Table for this generator. + * + * @return The table. + */ + public final Table getTable() { + return table; + } + /** * The name of the column in which we store the segment to which each row * belongs. The value here acts as PK. @@ -563,7 +573,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab public void registerExportables(Database database) { final Dialect dialect = database.getJdbcEnvironment().getDialect(); - org.hibernate.metamodel.spi.relational.Table table = database.getSchemaFor( qualifiedTableName ) + table = database.getSchemaFor( qualifiedTableName ) .createTable( qualifiedTableName.getName(), qualifiedTableName.getName() ); Column segmentColumn = table.createColumn( qualifiedSegmentColumnName ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java index a8fde6abdf..204213e1ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java @@ -25,7 +25,6 @@ package org.hibernate.metamodel.internal; import static org.hibernate.engine.spi.SyntheticAttributeHelper.SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -56,7 +55,9 @@ import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.id.EntityIdentifierNature; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentityGenerator; +import org.hibernate.id.MultipleHiLoPerTableGenerator; import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.id.enhanced.TableGenerator; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.FilterConfiguration; @@ -2887,7 +2888,8 @@ public class Binder { if ( UniqueConstraintSource.class.isInstance( constraintSource ) ) { UniqueKey uk = new UniqueKey(); - final TableSpecification table = entityBinding.locateTable( constraintSource.getTableName() ); + TableSpecification table = findUniqueConstraintTable( entityBinding, constraintSource.getTableName() ); + final List columnNames = constraintSource.columnNames(); final String constraintName = StringHelper.isEmpty( constraintSource.name() ) ? UniqueKey.generateName( table, columnNames.toArray( new String[columnNames.size()] ) ) @@ -2901,6 +2903,52 @@ public class Binder { } } } + + private TableSpecification findUniqueConstraintTable(EntityBinding entityBinding, String tableName) { + try { + return entityBinding.locateTable( tableName ); + } + catch ( AssertionFailure e ) { + Identifier identifier = Identifier.toIdentifier( tableName ); + + // TODO: Make TableGenerator & MultipleHiLoPerTableGenerator extend an abstract? + // @TableGenerator + IdentifierGenerator idGenerator = entityBinding.getHierarchyDetails().getEntityIdentifier().getIdentifierGenerator(); + if (idGenerator instanceof TableGenerator) { + Table generatorTable = ((TableGenerator) idGenerator).getTable(); + if (generatorTable != null && generatorTable.getLogicalName().equals( identifier ) ) { + return generatorTable; + } + } + else if (idGenerator instanceof MultipleHiLoPerTableGenerator) { + Table generatorTable = ((MultipleHiLoPerTableGenerator) idGenerator).getTable(); + if (generatorTable != null && generatorTable.getLogicalName().equals( identifier ) ) { + return generatorTable; + } + } + + // @JoinTable and @CollectionTable + Iterator attributeBindings = entityBinding.attributeBindings().iterator(); + while (attributeBindings.hasNext()) { + AttributeBinding attributeBinding = attributeBindings.next(); + if (attributeBinding instanceof PluralAttributeBinding) { + PluralAttributeBinding pluralAttributeBinding = (PluralAttributeBinding) attributeBinding; + + TableSpecification pluralTable = pluralAttributeBinding.getPluralAttributeKeyBinding().getCollectionTable(); + if (pluralTable != null && pluralTable.getLogicalName().equals( identifier ) ) { + return pluralTable; + } + } + } + } + + throw new AssertionFailure( + String.format( + "Unable to find locate table %s", + tableName + ) + ); + } private List bindValues( final AttributeBindingContainer attributeBindingContainer, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java index 9f8cb9c9d8..2a69bec5f1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java @@ -331,6 +331,36 @@ public class EntitySourceImpl implements EntitySource { } } + if ( entityClass.hostsAnnotation( JPADotNames.COLLECTION_TABLE ) ) { + List collectionTables = JandexHelper.getAnnotations( + entityClass.getClassInfo(), JPADotNames.COLLECTION_TABLE ); + for (AnnotationInstance collectionTable : collectionTables) { + String tableName = JandexHelper.getValue( collectionTable, "name", String.class ); + addUniqueConstraints( constraintSources, collectionTable, tableName ); + } + + } + + if ( entityClass.hostsAnnotation( JPADotNames.JOIN_TABLE ) ) { + List joinTables = JandexHelper.getAnnotations( + entityClass.getClassInfo(), JPADotNames.JOIN_TABLE ); + for (AnnotationInstance joinTable : joinTables) { + String tableName = JandexHelper.getValue( joinTable, "name", String.class ); + addUniqueConstraints( constraintSources, joinTable, tableName ); + } + + } + + if ( entityClass.hostsAnnotation( JPADotNames.TABLE_GENERATOR ) ) { + List tableGenerators = JandexHelper.getAnnotations( + entityClass.getClassInfo(), JPADotNames.TABLE_GENERATOR ); + for (AnnotationInstance tableGenerator : tableGenerators) { + String tableName = JandexHelper.getValue( tableGenerator, "table", String.class ); + addUniqueConstraints( constraintSources, tableGenerator, tableName ); + } + + } + return constraintSources; } diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/UniqueConstraintBindingTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/UniqueConstraintBindingTest.java index 0117dd6742..9c037c4ebe 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/UniqueConstraintBindingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/UniqueConstraintBindingTest.java @@ -24,44 +24,66 @@ package org.hibernate.metamodel.internal.source.annotations.entity; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -import org.junit.Test; - -import org.hibernate.metamodel.spi.binding.EntityBinding; -import org.hibernate.metamodel.spi.relational.TableSpecification; -import org.hibernate.metamodel.spi.relational.UniqueKey; -import org.hibernate.testing.junit4.BaseAnnotationBindingTestCase; -import org.hibernate.testing.junit4.Resources; - import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import java.util.Set; + +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.OrderColumn; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.UniqueConstraint; + +import org.hibernate.id.MultipleHiLoPerTableGenerator; +import org.hibernate.metamodel.spi.binding.EntityBinding; +import org.hibernate.metamodel.spi.relational.TableSpecification; +import org.hibernate.metamodel.spi.relational.UniqueKey; +import org.hibernate.test.util.SchemaUtil; +import org.hibernate.testing.junit4.BaseAnnotationBindingTestCase; +import org.hibernate.testing.junit4.Resources; +import org.junit.Test; + /** * test for {@link javax.persistence.UniqueConstraint} - * + * * @author Strong Liu + * @author Brett Meyer */ public class UniqueConstraintBindingTest extends BaseAnnotationBindingTestCase { @Test - @Resources(annotatedClasses = TableWithUniqueConstraint.class) + @Resources(annotatedClasses = { TableWithUniqueConstraint.class, SecondTable.class }) public void testTableUniqueConstraints() { EntityBinding binding = getEntityBinding( TableWithUniqueConstraint.class ); - TableSpecification table = binding.getPrimaryTable(); + testTableUniqueConstraints( binding.getPrimaryTable(), "u1", 2 ); + testTableUniqueConstraints( ( (MultipleHiLoPerTableGenerator) binding.getHierarchyDetails() + .getEntityIdentifier().getIdentifierGenerator() ).getTable(), "u2", 1 ); + testTableUniqueConstraints( SchemaUtil.getCollection( TableWithUniqueConstraint.class, "secondTables", meta ) + .getPluralAttributeKeyBinding().getCollectionTable(), "u3", 2 ); + testTableUniqueConstraints( SchemaUtil.getCollection( TableWithUniqueConstraint.class, "elements", meta ) + .getPluralAttributeKeyBinding().getCollectionTable(), "u4", 1 ); + } + + private void testTableUniqueConstraints(TableSpecification table, String ukName, int ukNumColumns) { Iterable uniqueKeyIterable = table.getUniqueKeys(); assertNotNull( uniqueKeyIterable ); int i = 0; for ( UniqueKey key : uniqueKeyIterable ) { i++; - assertEquals( "u1", key.getName() ); + assertEquals( ukName, key.getName() ); assertTrue( table == key.getTable() ); assertNotNull( key.getColumns() ); - assertEquals( "There should be two columns in the unique constraint", 2, key.getColumns().size() ); - assertEquals( "There should be two columns in the unique constraint", 2, key.getColumnSpan() ); + assertEquals( "There should be two columns in the unique constraint", ukNumColumns, key.getColumns().size() ); + assertEquals( "There should be two columns in the unique constraint", ukNumColumns, key.getColumnSpan() ); } assertEquals( "There should only be one unique constraint", 1, i ); } @@ -70,8 +92,36 @@ public class UniqueConstraintBindingTest extends BaseAnnotationBindingTestCase { @Table(uniqueConstraints = { @UniqueConstraint(name = "u1", columnNames = { "name", "age" }) }) class TableWithUniqueConstraint { @Id + @GeneratedValue(strategy = GenerationType.TABLE, generator = "fooGenerator") + @TableGenerator(name = "fooGenerator", table = "foo_generator_table", valueColumnName = "fooGeneratorValue", uniqueConstraints = @UniqueConstraint(columnNames = "fooGeneratorValue", name = "u2")) int id; + String name; + int age; + + @ManyToMany + @JoinTable(name = "JoinTable", joinColumns = @JoinColumn(name = "secondTable"), inverseJoinColumns = @JoinColumn(name = "tableWithUniqueConstraint"), uniqueConstraints = @UniqueConstraint(columnNames = { + "secondTable", "tableWithUniqueConstraint" }, name = "u3")) + Set secondTables; + + @ElementCollection + @CollectionTable(name = "CollectionTable", joinColumns = @JoinColumn(name = "element"), uniqueConstraints = @UniqueConstraint(columnNames = "element", name = "u4")) + @OrderColumn(name = "element_index") + public int[] elements; + } + + @Entity + class SecondTable { + @Id + @GeneratedValue + int id; + + String name; + + int age; + + @ManyToMany(mappedBy = "secondTables") + Set tableWithUniqueConstraint; } }