diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index daf65689f8..719e3798d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -1378,11 +1378,9 @@ public class Configuration implements Serializable { for ( Map.Entry> tableListEntry : uniqueConstraintHoldersByTable.entrySet() ) { final Table table = tableListEntry.getKey(); final List uniqueConstraints = tableListEntry.getValue(); - int uniqueIndexPerTable = 0; for ( UniqueConstraintHolder holder : uniqueConstraints ) { - uniqueIndexPerTable++; final String keyName = StringHelper.isEmpty( holder.getName() ) - ? "UK_" + table.getName() + "_" + uniqueIndexPerTable + ? StringHelper.randomFixedLengthHex("UK_") : holder.getName(); buildUniqueKeyFromColumnNames( table, keyName, holder.getColumns() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index f982ec33cb..ea3f476416 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.StringTokenizer; +import java.util.UUID; import org.hibernate.dialect.Dialect; import org.hibernate.internal.util.collections.ArrayHelper; @@ -758,4 +759,18 @@ public final class StringHelper { public static String[] toArrayElement(String s) { return ( s == null || s.length() == 0 ) ? new String[0] : new String[] { s }; } + + // Oracle restricts identifier lengths to 30. Rather than tie this to + // Dialect, simply restrict randomly-generated constrain names across + // the board. + private static final int MAX_NAME_LENGTH = 30; + public static String randomFixedLengthHex(String prefix) { + int length = MAX_NAME_LENGTH - prefix.length(); + String s = UUID.randomUUID().toString(); + s = s.replace( "-", "" ); + if (s.length() > length) { + s = s.substring( 0, length ); + } + return prefix + s; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 488ebaab33..05fbdd10aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -35,6 +35,7 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.Mapping; +import org.hibernate.internal.util.StringHelper; import org.hibernate.tool.hbm2ddl.ColumnMetadata; import org.hibernate.tool.hbm2ddl.TableMetadata; @@ -394,8 +395,6 @@ public class Table implements RelationalModel, Serializable { Iterator iter = getColumnIterator(); List results = new ArrayList(); - int uniqueIndexInteger = 0; - while ( iter.hasNext() ) { Column column = (Column) iter.next(); @@ -422,9 +421,8 @@ public class Table implements RelationalModel, Serializable { } if ( column.isUnique() ) { - uniqueIndexInteger++; UniqueKey uk = getOrCreateUniqueKey( - "UK_" + name + "_" + uniqueIndexInteger); + StringHelper.randomFixedLengthHex("UK_")); uk.addColumn( column ); alter.append( dialect.getUniqueDelegate() .applyUniqueToColumn( column ) ); @@ -493,7 +491,6 @@ public class Table implements RelationalModel, Serializable { } Iterator iter = getColumnIterator(); - int uniqueIndexInteger = 0; while ( iter.hasNext() ) { Column col = (Column) iter.next(); @@ -527,9 +524,8 @@ public class Table implements RelationalModel, Serializable { } if ( col.isUnique() ) { - uniqueIndexInteger++; UniqueKey uk = getOrCreateUniqueKey( - "uc_" + name + "_" + uniqueIndexInteger); + StringHelper.randomFixedLengthHex("UK_")); uk.addColumn( col ); buf.append( dialect.getUniqueDelegate() .applyUniqueToColumn( col ) ); @@ -625,7 +621,7 @@ public class Table implements RelationalModel, Serializable { } public UniqueKey createUniqueKey(List keyColumns) { - String keyName = "UK_" + uniqueColumnString( keyColumns.iterator() ); + String keyName = StringHelper.randomFixedLengthHex("UK_"); UniqueKey uk = getOrCreateUniqueKey( keyName ); uk.addColumns( keyColumns.iterator() ); return uk; @@ -665,9 +661,7 @@ public class Table implements RelationalModel, Serializable { fk.setName( keyName ); } else { - fk.setName( "FK" + uniqueColumnString( keyColumns.iterator(), referencedEntityName ) ); - //TODO: add referencedClass to disambiguate to FKs on the same - // columns, pointing to different tables + fk.setName( StringHelper.randomFixedLengthHex("FK_") ); } fk.setTable( this ); foreignKeys.put( key, fk ); @@ -686,22 +680,6 @@ public class Table implements RelationalModel, Serializable { } - public String uniqueColumnString(Iterator iterator) { - return uniqueColumnString( iterator, null ); - } - - public String uniqueColumnString(Iterator iterator, String referencedEntityName) { - int result = 0; - if ( referencedEntityName != null ) { - result += referencedEntityName.hashCode(); - } - while ( iterator.hasNext() ) { - result += iterator.next().hashCode(); - } - return ( Integer.toHexString( name.hashCode() ) + Integer.toHexString( result ) ).toUpperCase(); - } - - public String getSchema() { return schema; diff --git a/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java b/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java index 5685d986d8..0e33a4ff83 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java @@ -20,57 +20,100 @@ */ package org.hibernate.test.constraint; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.Iterator; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.OneToOne; import javax.persistence.Table; +import javax.persistence.UniqueConstraint; import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.UniqueKey; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; -/** - * HHH-7797 re-wrote the way dialects handle unique constraints. Test - * variations of unique & not null to ensure the constraints are created - * correctly for each dialect. - * - * @author Brett Meyer - */ -@TestForIssue( jiraKey = "HHH-7797" ) public class ConstraintTest extends BaseCoreFunctionalTestCase { + private static final int MAX_NAME_LENGTH = 30; + + private static final String EXPLICIT_FK_NAME = "fk_explicit"; + + private static final String EXPLICIT_UK_NAME = "uk_explicit"; + @Override protected Class[] getAnnotatedClasses() { return new Class[] { - Entity1.class + DataPoint.class, DataPoint2.class }; } @Test - public void testConstraints() { - Column column = (Column) configuration().getClassMapping( Entity1.class.getName() ) + @TestForIssue( jiraKey = "HHH-7797" ) + public void testUniqueConstraints() { + Column column = (Column) configuration().getClassMapping( DataPoint.class.getName() ) .getProperty( "foo1" ).getColumnIterator().next(); assertFalse( column.isNullable() ); assertTrue( column.isUnique() ); - column = (Column) configuration().getClassMapping( Entity1.class.getName() ) + column = (Column) configuration().getClassMapping( DataPoint.class.getName() ) .getProperty( "foo2" ).getColumnIterator().next(); assertTrue( column.isNullable() ); assertTrue( column.isUnique() ); - column = (Column) configuration().getClassMapping( Entity1.class.getName() ) + column = (Column) configuration().getClassMapping( DataPoint.class.getName() ) .getProperty( "id" ).getColumnIterator().next(); assertFalse( column.isNullable() ); assertTrue( column.isUnique() ); } + @Test + @TestForIssue( jiraKey = "HHH-1904" ) + public void testConstraintNameLength() { + Iterator tableItr = configuration().getTableMappings(); + while (tableItr.hasNext()) { + org.hibernate.mapping.Table table = tableItr.next(); + + Iterator fkItr = table.getForeignKeyIterator(); + while (fkItr.hasNext()) { + ForeignKey fk = (ForeignKey) fkItr.next(); + assertTrue( fk.getName().length() <= MAX_NAME_LENGTH ); + + // ensure the randomly generated constraint name doesn't + // happen if explicitly given + Column column = fk.getColumn( 0 ); + if ( column.getName().equals( "explicit" ) ) { + assertEquals( fk.getName(), EXPLICIT_FK_NAME ); + } + } + + Iterator ukItr = table.getUniqueKeyIterator(); + while (ukItr.hasNext()) { + UniqueKey uk = (UniqueKey) ukItr.next(); + assertTrue( uk.getName().length() <= MAX_NAME_LENGTH ); + + // ensure the randomly generated constraint name doesn't + // happen if explicitly given + Column column = uk.getColumn( 0 ); + if ( column.getName().equals( "explicit" ) ) { + assertEquals( uk.getName(), EXPLICIT_UK_NAME ); + } + } + } + } + @Entity - @Table( name = "Entity1" ) - public static class Entity1 { + @Table( name = "DataPoint", uniqueConstraints = { + @UniqueConstraint( name = EXPLICIT_UK_NAME, columnNames = { "explicit" } ) + } ) + public static class DataPoint { @Id @GeneratedValue @javax.persistence.Column( nullable = false, unique = true) @@ -81,5 +124,22 @@ public class ConstraintTest extends BaseCoreFunctionalTestCase { @javax.persistence.Column( nullable = true, unique = true) public String foo2; + + public String explicit; + } + + @Entity + @Table( name = "DataPoint2" ) + public static class DataPoint2 { + @Id + @GeneratedValue + public long id; + + @OneToOne + public DataPoint dp; + + @OneToOne + @org.hibernate.annotations.ForeignKey(name = EXPLICIT_FK_NAME) + public DataPoint explicit; } } \ No newline at end of file