HHH-7797 Use unique indexes on nullable columns for DB2. Correctly

handle @UniqueConstraint table annotations on second passes.
This commit is contained in:
brmeyer 2012-12-17 13:48:12 -05:00 committed by Brett Meyer
parent 2cd062058c
commit e629feee8a
10 changed files with 162 additions and 25 deletions

View File

@ -1540,7 +1540,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<Column> unbound = new HashSet<Column>();
@ -1559,8 +1558,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 );
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 "";
}

View File

@ -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,

View File

@ -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" :

View File

@ -425,8 +425,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() ) {
@ -526,7 +526,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 (" )

View File

@ -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;
// }
}

View File

@ -63,7 +63,6 @@ public class Index extends AbstractConstraint implements Constraint {
Iterable<Column> 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;

View File

@ -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() ) {