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 f5e59e02f2..48dfaf6e1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -1544,7 +1544,6 @@ public class Configuration implements Serializable { private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames) { keyName = normalizer.normalizeIdentifierQuoting( keyName ); - UniqueKey uc; int size = columnNames.length; Column[] columns = new Column[size]; Set unbound = new HashSet(); @@ -1563,8 +1562,10 @@ public class Configuration implements Serializable { } for ( Column column : columns ) { if ( table.containsColumn( column ) ) { - uc = table.getOrCreateUniqueKey( keyName ); - uc.addColumn( table.getColumn( column ) ); + Column tableColumn = table.getColumn( column ); + tableColumn.setUnique( true ); + Dialect.getDialect().getUniqueDelegate().generateUniqueKey( + table, tableColumn ); unbound.remove( column ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 01f13b7883..61f1576f10 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -35,6 +35,8 @@ import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction; +import org.hibernate.dialect.unique.DB2UniqueDelegate; +import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.internal.util.JdbcExceptionHelper; @@ -48,6 +50,8 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; * @author Gavin King */ public class DB2Dialect extends Dialect { + + private final UniqueDelegate uniqueDelegate; public DB2Dialect() { super(); @@ -174,6 +178,8 @@ public class DB2Dialect extends Dialect { registerKeyword( "only" ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH ); + + uniqueDelegate = new DB2UniqueDelegate( this ); } @Override public String getLowercaseFunction() { @@ -451,5 +457,10 @@ public class DB2Dialect extends Dialect { } }; } + + @Override + public UniqueDelegate getUniqueDelegate() { + return uniqueDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java new file mode 100644 index 0000000000..852b7c7854 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java @@ -0,0 +1,109 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.dialect.unique; + +import java.util.Iterator; + +import org.hibernate.dialect.Dialect; +import org.hibernate.metamodel.relational.Column; +import org.hibernate.metamodel.relational.Index; +import org.hibernate.metamodel.relational.UniqueKey; + +/** + * DB2 does not allow unique constraints on nullable columns. Rather than + * forcing "not null", use unique *indexes* instead. + * + * @author Brett Meyer + */ +public class DB2UniqueDelegate extends DefaultUniqueDelegate { + + public DB2UniqueDelegate( Dialect dialect ) { + super( dialect ); + } + + @Override + public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ) { + if ( hasNullable( uniqueKey ) ) { + return org.hibernate.mapping.Index.buildSqlCreateIndexString( + dialect, uniqueKey.getName(), uniqueKey.getTable(), + uniqueKey.columnIterator(), true, defaultCatalog, + defaultSchema ); + } else { + return super.applyUniquesOnAlter( + uniqueKey, defaultCatalog, defaultSchema ); + } + } + + @Override + public String applyUniquesOnAlter( UniqueKey uniqueKey ) { + if ( hasNullable( uniqueKey ) ) { + return Index.buildSqlCreateIndexString( + dialect, uniqueKey.getName(), uniqueKey.getTable(), + uniqueKey.getColumns(), true ); + } else { + return super.applyUniquesOnAlter( uniqueKey ); + } + } + + @Override + public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ) { + if ( hasNullable( uniqueKey ) ) { + return org.hibernate.mapping.Index.buildSqlDropIndexString( + dialect, uniqueKey.getTable(), uniqueKey.getName(), + defaultCatalog, defaultSchema ); + } else { + return super.dropUniquesOnAlter( + uniqueKey, defaultCatalog, defaultSchema ); + } + } + + @Override + public String dropUniquesOnAlter( UniqueKey uniqueKey ) { + if ( hasNullable( uniqueKey ) ) { + return Index.buildSqlDropIndexString( + dialect, uniqueKey.getTable(), uniqueKey.getName() ); + } else { + return super.dropUniquesOnAlter( uniqueKey ); + } + } + + private boolean hasNullable( org.hibernate.mapping.UniqueKey uniqueKey ) { + Iterator iter = uniqueKey.getColumnIterator(); + while ( iter.hasNext() ) { + if ( ( ( org.hibernate.mapping.Column ) iter.next() ).isNullable() ) { + return true; + } + } + return false; + } + + private boolean hasNullable( UniqueKey uniqueKey ) { + Iterator iter = uniqueKey.getColumns().iterator(); + while ( iter.hasNext() ) { + if ( ( ( Column ) iter.next() ).isNullable() ) { + return true; + } + } + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index aabdcd57a1..41678aa5b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -35,7 +35,7 @@ import org.hibernate.metamodel.relational.UniqueKey; */ public class DefaultUniqueDelegate implements UniqueDelegate { - private final Dialect dialect; + protected final Dialect dialect; public DefaultUniqueDelegate( Dialect dialect ) { this.dialect = dialect; @@ -57,7 +57,12 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public String applyUniqueToColumn() { + public String applyUniqueToColumn( org.hibernate.mapping.Column column ) { + return ""; + } + + @Override + public String applyUniqueToColumn( Column column ) { return ""; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index fc448645a4..ef3728361d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -68,9 +68,20 @@ public interface UniqueDelegate { * return the syntax necessary to mutate the column definition * (usually "unique"). * + * @param column * @return String */ - public String applyUniqueToColumn(); + public String applyUniqueToColumn( org.hibernate.mapping.Column column ); + + /** + * If the dialect does not supports unique constraints, this method should + * return the syntax necessary to mutate the column definition + * (usually "unique"). + * + * @param column + * @return String + */ + public String applyUniqueToColumn( Column column ); /** * If constraints are supported, but not in seperate alter statements, diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Index.java b/hibernate-core/src/main/java/org/hibernate/mapping/Index.java index f91fc55694..da70576a05 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Index.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Index.java @@ -79,7 +79,6 @@ public class Index implements RelationalModel, Serializable { String defaultCatalog, String defaultSchema ) { - //TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far StringBuilder buf = new StringBuilder( "create" ) .append( unique ? " unique" : 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 af49653db4..8aaf8e0064 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -424,8 +424,8 @@ public class Table implements RelationalModel, Serializable { if ( column.isUnique() ) { dialect.getUniqueDelegate().generateUniqueKey( this, column ); - alter.append( - dialect.getUniqueDelegate().applyUniqueToColumn() ); + alter.append( dialect.getUniqueDelegate() + .applyUniqueToColumn( column ) ); } if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) { @@ -525,7 +525,9 @@ public class Table implements RelationalModel, Serializable { if ( col.isUnique() ) { dialect.getUniqueDelegate().generateUniqueKey( this, col ); - buf.append( dialect.getUniqueDelegate().applyUniqueToColumn() ); } + buf.append( dialect.getUniqueDelegate() + .applyUniqueToColumn( col ) ); + } if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { buf.append( " check (" ) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java index bf4bf07eda..e5613817b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java @@ -57,17 +57,4 @@ public class UniqueKey extends Constraint { this, defaultCatalog, defaultSchema ); } -// @Override -// public boolean isGenerated(Dialect dialect) { -// if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return false; -// if ( dialect.supportsNotNullUnique() ) return true; -// -// Iterator iter = getColumnIterator(); -// while ( iter.hasNext() ) { -// // Dialect does not support "not null unique" and this column is not null. -// if ( ! ( (Column) iter.next() ).isNullable() ) return false; -// } -// return true; -// } - } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java index 20b4356ebc..2eb119c914 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java @@ -63,7 +63,6 @@ public class Index extends AbstractConstraint implements Constraint { Iterable columns, boolean unique ) { - //TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far StringBuilder buf = new StringBuilder( "create" ) .append( unique ? " unique" : @@ -89,6 +88,18 @@ public class Index extends AbstractConstraint implements Constraint { return buf.toString(); } + public static String buildSqlDropIndexString( + Dialect dialect, + TableSpecification table, + String name + ) { + return "drop index " + + StringHelper.qualify( + table.getQualifiedName( dialect ), + name + ); + } + public String sqlConstraintStringInAlterTable(Dialect dialect) { StringBuilder buf = new StringBuilder( " index (" ); boolean first = true; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 76f12e77d8..69cdb3cf0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -201,7 +201,8 @@ public class Table extends AbstractTableSpecification implements Exportable { if ( col.isUnique() ) { dialect.getUniqueDelegate().generateUniqueKey( this, col ); - buf.append( dialect.getUniqueDelegate().applyUniqueToColumn() ); + buf.append( dialect.getUniqueDelegate() + .applyUniqueToColumn( col ) ); } if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) {