HHH-7797 Use unique indexes on nullable columns for DB2. Correctly
handle @UniqueConstraint table annotations on second passes.
This commit is contained in:
parent
b4e8c5a83b
commit
41397f22d1
|
@ -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<Column> unbound = new HashSet<Column>();
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -49,6 +51,8 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
|||
*/
|
||||
public class DB2Dialect extends Dialect {
|
||||
|
||||
private final UniqueDelegate uniqueDelegate;
|
||||
|
||||
public DB2Dialect() {
|
||||
super();
|
||||
registerColumnType( Types.BIT, "smallint" );
|
||||
|
@ -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() {
|
||||
|
@ -452,4 +458,9 @@ public class DB2Dialect extends Dialect {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 "";
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" :
|
||||
|
|
|
@ -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 (" )
|
||||
|
|
|
@ -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;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() ) {
|
||||
|
|
Loading…
Reference in New Issue