HHH-8149 Revert HHH-7797 in 4.1.x

This commit is contained in:
Brett Meyer 2013-04-05 12:52:31 -04:00
parent 5dd220393c
commit 40aaaa9ca7
23 changed files with 285 additions and 634 deletions

View File

@ -50,7 +50,6 @@ import java.util.StringTokenizer;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.MapsId; import javax.persistence.MapsId;
@ -59,6 +58,10 @@ import org.dom4j.Attribute;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
import org.dom4j.Element; import org.dom4j.Element;
import org.jboss.logging.Logger;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.DuplicateMappingException; import org.hibernate.DuplicateMappingException;
import org.hibernate.EmptyInterceptor; import org.hibernate.EmptyInterceptor;
@ -142,9 +145,6 @@ import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver; import org.hibernate.type.TypeResolver;
import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;
import org.jboss.logging.Logger;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
/** /**
* An instance of <tt>Configuration</tt> allows the application * An instance of <tt>Configuration</tt> allows the application
@ -1045,15 +1045,17 @@ public class Configuration implements Serializable {
Table table = (Table) iter.next(); Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) { if ( table.isPhysicalTable() ) {
Iterator subIter = table.getUniqueKeyIterator(); if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) {
while ( subIter.hasNext() ) { Iterator subIter = table.getUniqueKeyIterator();
UniqueKey uk = (UniqueKey) subIter.next(); while ( subIter.hasNext() ) {
String constraintString = uk.sqlCreateString( dialect, mapping, defaultCatalog, defaultSchema ); UniqueKey uk = (UniqueKey) subIter.next();
if (constraintString != null) script.add( constraintString ); String constraintString = uk.sqlCreateString( dialect, mapping, defaultCatalog, defaultSchema );
if (constraintString != null) script.add( constraintString );
}
} }
subIter = table.getIndexIterator(); Iterator subIter = table.getIndexIterator();
while ( subIter.hasNext() ) { while ( subIter.hasNext() ) {
Index index = (Index) subIter.next(); Index index = (Index) subIter.next();
script.add( script.add(
@ -1224,6 +1226,15 @@ public class Configuration implements Serializable {
) )
); );
} }
//broken, 'cos we don't generate these with names in SchemaExport
// subIter = table.getUniqueKeyIterator();
// while ( subIter.hasNext() ) {
// UniqueKey uk = (UniqueKey) subIter.next();
// if ( tableInfo==null || tableInfo.getIndexMetadata( uk.getFilterName() ) == null ) {
// script.add( uk.sqlCreateString(dialect, mapping) );
// }
// }
} }
} }
@ -1531,6 +1542,7 @@ public class Configuration implements Serializable {
private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames) { private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames) {
keyName = normalizer.normalizeIdentifierQuoting( keyName ); keyName = normalizer.normalizeIdentifierQuoting( keyName );
UniqueKey uc;
int size = columnNames.length; int size = columnNames.length;
Column[] columns = new Column[size]; Column[] columns = new Column[size];
Set<Column> unbound = new HashSet<Column>(); Set<Column> unbound = new HashSet<Column>();
@ -1547,10 +1559,10 @@ public class Configuration implements Serializable {
unboundNoLogical.add( new Column( column ) ); unboundNoLogical.add( new Column( column ) );
} }
} }
UniqueKey uk = table.getOrCreateUniqueKey( keyName );
for ( Column column : columns ) { for ( Column column : columns ) {
if ( table.containsColumn( column ) ) { if ( table.containsColumn( column ) ) {
uk.addColumn( column ); uc = table.getOrCreateUniqueKey( keyName );
uc.addColumn( table.getColumn( column ) );
unbound.remove( column ); unbound.remove( column );
} }
} }

View File

@ -401,6 +401,11 @@ public class Cache71Dialect extends Dialect {
return false; return false;
} }
public boolean supportsUnique() {
// Does this dialect support the UNIQUE column syntax?
return true;
}
/** /**
* The syntax used to add a foreign key constraint to a table. * The syntax used to add a foreign key constraint to a table.
* *

View File

@ -30,13 +30,12 @@ import java.sql.Types;
import org.hibernate.JDBCException; import org.hibernate.JDBCException;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.AnsiTrimEmulationFunction;
import org.hibernate.dialect.function.AvgWithArgumentCastFunction; import org.hibernate.dialect.function.AvgWithArgumentCastFunction;
import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction; 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.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
@ -50,8 +49,6 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
* @author Gavin King * @author Gavin King
*/ */
public class DB2Dialect extends Dialect { public class DB2Dialect extends Dialect {
private final UniqueDelegate uniqueDelegate;
public DB2Dialect() { public DB2Dialect() {
super(); super();
@ -178,8 +175,6 @@ public class DB2Dialect extends Dialect {
registerKeyword( "only" ); registerKeyword( "only" );
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH );
uniqueDelegate = new DB2UniqueDelegate( this );
} }
@Override @Override
public String getLowercaseFunction() { public String getLowercaseFunction() {
@ -284,6 +279,10 @@ public class DB2Dialect extends Dialect {
return false; return false;
} }
@Override @Override
public boolean supportsNotNullUnique() {
return false;
}
@Override
public boolean supportsExistsInSelect() { public boolean supportsExistsInSelect() {
return false; return false;
} }
@ -458,11 +457,6 @@ public class DB2Dialect extends Dialect {
}; };
} }
@Override
public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate;
}
@Override @Override
public String getNotExpression( String expression ) { public String getNotExpression( String expression ) {
return "not (" + expression + ")"; return "not (" + expression + ")";

View File

@ -58,8 +58,6 @@ import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy;
import org.hibernate.dialect.lock.SelectLockingStrategy; import org.hibernate.dialect.lock.SelectLockingStrategy;
import org.hibernate.dialect.pagination.LegacyLimitHandler; import org.hibernate.dialect.pagination.LegacyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.RowSelection;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
@ -117,8 +115,6 @@ public abstract class Dialect implements ConversionContext {
private final Properties properties = new Properties(); private final Properties properties = new Properties();
private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>(); private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>();
private final Set<String> sqlKeywords = new HashSet<String>(); private final Set<String> sqlKeywords = new HashSet<String>();
private final UniqueDelegate uniqueDelegate;
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -207,8 +203,6 @@ public abstract class Dialect implements ConversionContext {
registerHibernateType( Types.BLOB, StandardBasicTypes.BLOB.getName() ); registerHibernateType( Types.BLOB, StandardBasicTypes.BLOB.getName() );
registerHibernateType( Types.CLOB, StandardBasicTypes.CLOB.getName() ); registerHibernateType( Types.CLOB, StandardBasicTypes.CLOB.getName() );
registerHibernateType( Types.REAL, StandardBasicTypes.FLOAT.getName() ); registerHibernateType( Types.REAL, StandardBasicTypes.FLOAT.getName() );
uniqueDelegate = new DefaultUniqueDelegate( this );
} }
/** /**
@ -1834,6 +1828,23 @@ public abstract class Dialect implements ConversionContext {
return true; return true;
} }
/**
* Does this dialect support the <tt>UNIQUE</tt> column syntax?
*
* @return boolean
*/
public boolean supportsUnique() {
return true;
}
/**
* Does this dialect support adding Unique constraints via create and alter table ?
* @return boolean
*/
public boolean supportsUniqueConstraintInCreateAlterTable() {
return true;
}
/** /**
* The syntax used to add a column to a table (optional). * The syntax used to add a column to a table (optional).
* *
@ -1899,6 +1910,16 @@ public abstract class Dialect implements ConversionContext {
return " add constraint " + constraintName + " primary key "; return " add constraint " + constraintName + " primary key ";
} }
/**
* The syntax used to add a unique constraint to a table.
*
* @param constraintName The name of the unique constraint.
* @return The "add unique" fragment
*/
public String getAddUniqueConstraintString(String constraintName) {
return " add constraint " + constraintName + " unique ";
}
public boolean hasSelfReferentialForeignKeyBug() { public boolean hasSelfReferentialForeignKeyBug() {
return false; return false;
} }
@ -1968,6 +1989,10 @@ public abstract class Dialect implements ConversionContext {
return true; return true;
} }
public boolean supportsNotNullUnique() {
return true;
}
/** /**
* Completely optional cascading drop clause * Completely optional cascading drop clause
* *
@ -2320,56 +2345,7 @@ public abstract class Dialect implements ConversionContext {
return false; return false;
} }
public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate;
}
public String getNotExpression( String expression ) { public String getNotExpression( String expression ) {
return "not " + expression; return "not " + expression;
} }
/**
* Does this dialect support the <tt>UNIQUE</tt> column syntax?
*
* @return boolean
*
* @deprecated {@link #getUniqueDelegate()} should be overridden instead.
*/
@Deprecated
public boolean supportsUnique() {
return true;
}
/**
* Does this dialect support adding Unique constraints via create and alter table ?
*
* @return boolean
*
* @deprecated {@link #getUniqueDelegate()} should be overridden instead.
*/
@Deprecated
public boolean supportsUniqueConstraintInCreateAlterTable() {
return true;
}
/**
* The syntax used to add a unique constraint to a table.
*
* @param constraintName The name of the unique constraint.
* @return The "add unique" fragment
*
* @deprecated {@link #getUniqueDelegate()} should be overridden instead.
*/
@Deprecated
public String getAddUniqueConstraintString(String constraintName) {
return " add constraint " + constraintName + " unique ";
}
/**
* @deprecated {@link #getUniqueDelegate()} should be overridden instead.
*/
@Deprecated
public boolean supportsNotNullUnique() {
return true;
}
} }

View File

@ -216,6 +216,10 @@ public class H2Dialect extends Dialect {
return " for update"; return " for update";
} }
public boolean supportsUnique() {
return true;
}
public boolean supportsLimit() { public boolean supportsLimit() {
return true; return true;
} }

View File

@ -246,6 +246,10 @@ public class HSQLDialect extends Dialect {
return ""; return "";
} }
public boolean supportsUnique() {
return false;
}
public boolean supportsLimit() { public boolean supportsLimit() {
return true; return true;
} }

View File

@ -306,6 +306,13 @@ public class IngresDialect extends Dialect {
return true; return true;
} }
/**
* Ingres explicitly needs "unique not null", because "with null" is default
*/
public boolean supportsNotNullUnique() {
return false;
}
/** /**
* Does this dialect support temporary tables? * Does this dialect support temporary tables?
*/ */

View File

@ -240,6 +240,13 @@ public class RDMSOS2200Dialect extends Dialect {
return ""; // Original Dialect.java returns " for update"; return ""; // Original Dialect.java returns " for update";
} }
/**
* RDMS does not support adding Unique constraints via create and alter table.
*/
public boolean supportsUniqueConstraintInCreateAlterTable() {
return true;
}
// Verify the state of this new method in Hibernate 3.0 Dialect.java // Verify the state of this new method in Hibernate 3.0 Dialect.java
/** /**
* RDMS does not support Cascade Deletes. * RDMS does not support Cascade Deletes.

View File

@ -422,6 +422,10 @@ public class SybaseASE15Dialect extends SybaseDialect {
return false; return false;
} }
public boolean supportsUniqueConstraintInCreateAlterTable() {
return false;
}
public String getCrossJoinSeparator() { public String getCrossJoinSeparator() {
return ", "; return ", ";
} }

View File

@ -102,6 +102,14 @@ public class TimesTenDialect extends Dialect {
public boolean qualifyIndexName() { public boolean qualifyIndexName() {
return false; return false;
} }
public boolean supportsUnique() {
return false;
}
public boolean supportsUniqueConstraintInCreateAlterTable() {
return false;
}
public String getAddColumnString() { public String getAddColumnString() {
return "add"; return "add";

View File

@ -1,109 +0,0 @@
/*
* 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

@ -1,148 +0,0 @@
/*
* 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.Table;
import org.hibernate.metamodel.relational.UniqueKey;
/**
* The default UniqueDelegate implementation for most dialects. Uses
* separate create/alter statements to apply uniqueness to a column.
*
* @author Brett Meyer
*/
public class DefaultUniqueDelegate implements UniqueDelegate {
protected final Dialect dialect;
public DefaultUniqueDelegate( Dialect dialect ) {
this.dialect = dialect;
}
@Override
public String applyUniqueToColumn( org.hibernate.mapping.Column column ) {
return "";
}
@Override
public String applyUniqueToColumn( Column column ) {
return "";
}
@Override
public String applyUniquesToTable( org.hibernate.mapping.Table table ) {
return "";
}
@Override
public String applyUniquesToTable( Table table ) {
return "";
}
@Override
public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey,
String defaultCatalog, String defaultSchema ) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
return new StringBuilder( "alter table " )
.append( uniqueKey.getTable().getQualifiedName(
dialect, defaultCatalog, defaultSchema ) )
.append( " add constraint " )
.append( uniqueKey.getName() )
.append( uniqueConstraintSql( uniqueKey ) )
.toString();
}
@Override
public String applyUniquesOnAlter( UniqueKey uniqueKey ) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
return new StringBuilder( "alter table " )
.append( uniqueKey.getTable().getQualifiedName( dialect ) )
.append( " add constraint " )
.append( uniqueKey.getName() )
.append( uniqueConstraintSql( uniqueKey ) )
.toString();
}
@Override
public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey,
String defaultCatalog, String defaultSchema ) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
return new StringBuilder( "alter table " )
.append( uniqueKey.getTable().getQualifiedName(
dialect, defaultCatalog, defaultSchema ) )
.append( " drop constraint " )
.append( dialect.quote( uniqueKey.getName() ) )
.toString();
}
@Override
public String dropUniquesOnAlter( UniqueKey uniqueKey ) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
return new StringBuilder( "alter table " )
.append( uniqueKey.getTable().getQualifiedName( dialect ) )
.append( " drop constraint " )
.append( dialect.quote( uniqueKey.getName() ) )
.toString();
}
@Override
public String uniqueConstraintSql( org.hibernate.mapping.UniqueKey uniqueKey ) {
StringBuilder sb = new StringBuilder();
sb.append( " unique (" );
Iterator columnIterator = uniqueKey.getColumnIterator();
while ( columnIterator.hasNext() ) {
org.hibernate.mapping.Column column
= (org.hibernate.mapping.Column) columnIterator.next();
sb.append( column.getQuotedName( dialect ) );
if ( columnIterator.hasNext() ) {
sb.append( ", " );
}
}
return sb.append( ')' ).toString();
}
@Override
public String uniqueConstraintSql( UniqueKey uniqueKey ) {
StringBuilder sb = new StringBuilder();
sb.append( " unique (" );
Iterator columnIterator = uniqueKey.getColumns().iterator();
while ( columnIterator.hasNext() ) {
org.hibernate.mapping.Column column
= (org.hibernate.mapping.Column) columnIterator.next();
sb.append( column.getQuotedName( dialect ) );
if ( columnIterator.hasNext() ) {
sb.append( ", " );
}
}
return sb.append( ')' ).toString();
}
}

View File

@ -1,146 +0,0 @@
/*
* 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 org.hibernate.metamodel.relational.Column;
import org.hibernate.metamodel.relational.Table;
import org.hibernate.metamodel.relational.UniqueKey;
/**
* Dialect-level delegate in charge of applying "uniqueness" to a column.
* Uniqueness can be defined in 1 of 3 ways:
*
* 1.) Add a unique constraint via separate alter table statements.
* 2.) Add a unique constraint via dialect-specific syntax in table create statement.
* 3.) Add "unique" syntax to the column itself.
*
* #1 & #2 are preferred, if possible -- #3 should be solely a fall-back.
*
* TODO: This could eventually be simplified. With AST, 1 "applyUniqueness"
* method might be possible. But due to .cfg and .mapping still resolving
* around StringBuilders, separate methods were needed.
*
* See HHH-7797.
*
* @author Brett Meyer
*/
public interface UniqueDelegate {
/**
* 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( 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,
* return uniqueConstraintSql in order to add the constraint to the
* original table definition.
*
* @param table
* @return String
*/
public String applyUniquesToTable( org.hibernate.mapping.Table table );
/**
* If constraints are supported, but not in seperate alter statements,
* return uniqueConstraintSql in order to add the constraint to the
* original table definition.
*
* @param table
* @return String
*/
public String applyUniquesToTable( Table table );
/**
* If creating unique constraints in separate alter statements is
* supported, generate the necessary "alter" syntax for the given key.
*
* @param uniqueKey
* @param defaultCatalog
* @param defaultSchema
* @return String
*/
public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey,
String defaultCatalog, String defaultSchema );
/**
* If creating unique constraints in separate alter statements is
* supported, generate the necessary "alter" syntax for the given key.
*
* @param uniqueKey
* @return String
*/
public String applyUniquesOnAlter( UniqueKey uniqueKey );
/**
* If dropping unique constraints in separate alter statements is
* supported, generate the necessary "alter" syntax for the given key.
*
* @param uniqueKey
* @param defaultCatalog
* @param defaultSchema
* @return String
*/
public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey,
String defaultCatalog, String defaultSchema );
/**
* If dropping unique constraints in separate alter statements is
* supported, generate the necessary "alter" syntax for the given key.
*
* @param uniqueKey
* @return String
*/
public String dropUniquesOnAlter( UniqueKey uniqueKey );
/**
* Generates the syntax necessary to create the unique constraint (reused
* by all methods). Ex: "unique (column1, column2, ...)"
*
* @param uniqueKey
* @return String
*/
public String uniqueConstraintSql( org.hibernate.mapping.UniqueKey uniqueKey );
/**
* Generates the syntax necessary to create the unique constraint (reused
* by all methods). Ex: "unique (column1, column2, ...)"
*
* @param uniqueKey
* @return String
*/
public String uniqueConstraintSql( UniqueKey uniqueKey );
}

View File

@ -79,6 +79,7 @@ public class Index implements RelationalModel, Serializable {
String defaultCatalog, String defaultCatalog,
String defaultSchema String defaultSchema
) { ) {
//TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far
StringBuilder buf = new StringBuilder( "create" ) StringBuilder buf = new StringBuilder( "create" )
.append( unique ? .append( unique ?
" unique" : " unique" :

View File

@ -26,6 +26,7 @@ package org.hibernate.mapping;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -422,13 +423,11 @@ public class Table implements RelationalModel, Serializable {
alter.append( " not null" ); alter.append( " not null" );
} }
if ( column.isUnique() ) { boolean useUniqueConstraint = column.isUnique() &&
uniqueIndexInteger++; dialect.supportsUnique() &&
UniqueKey uk = getOrCreateUniqueKey( ( column.isNullable() || dialect.supportsNotNullUnique() );
"UK_" + name + "_" + uniqueIndexInteger); if ( useUniqueConstraint ) {
uk.addColumn( column ); alter.append( " unique" );
alter.append( dialect.getUniqueDelegate()
.applyUniqueToColumn( column ) );
} }
if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) { if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
@ -526,16 +525,20 @@ public class Table implements RelationalModel, Serializable {
} }
} }
if ( col.isUnique() ) { boolean useUniqueConstraint = col.isUnique() &&
uniqueIndexInteger++; ( col.isNullable() || dialect.supportsNotNullUnique() );
UniqueKey uk = getOrCreateUniqueKey( if ( useUniqueConstraint ) {
"uc_" + name + "_" + uniqueIndexInteger); if ( dialect.supportsUnique() ) {
uk.addColumn( col ); buf.append( " unique" );
buf.append( dialect.getUniqueDelegate() }
.applyUniqueToColumn( col ) ); else {
UniqueKey uk = getOrCreateUniqueKey(
"UK_" + name + '_' + uniqueIndexInteger );
uk.addColumn( col );
}
} }
if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) {
buf.append( " check (" ) buf.append( " check (" )
.append( col.getCheckConstraint() ) .append( col.getCheckConstraint() )
@ -557,7 +560,21 @@ public class Table implements RelationalModel, Serializable {
.append( getPrimaryKey().sqlConstraintString( dialect ) ); .append( getPrimaryKey().sqlConstraintString( dialect ) );
} }
buf.append( dialect.getUniqueDelegate().applyUniquesToTable( this ) ); if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
Iterator ukiter = getUniqueKeyIterator();
while ( ukiter.hasNext() ) {
UniqueKey uk = (UniqueKey) ukiter.next();
String constraint = uk.sqlConstraintString( dialect );
if ( constraint != null ) {
buf.append( ", " ).append( constraint );
}
}
}
/*Iterator idxiter = getIndexIterator();
while ( idxiter.hasNext() ) {
Index idx = (Index) idxiter.next();
buf.append(',').append( idx.sqlConstraintString(dialect) );
}*/
if ( dialect.supportsTableCheck() ) { if ( dialect.supportsTableCheck() ) {
Iterator chiter = checkConstraints.iterator(); Iterator chiter = checkConstraints.iterator();

View File

@ -22,39 +22,89 @@
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*/ */
package org.hibernate.mapping; package org.hibernate.mapping;
import java.util.Iterator;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.Mapping;
/** /**
* A relational unique key constraint * A relational unique key constraint
* *
* @author Brett Meyer * @author Gavin King
*/ */
public class UniqueKey extends Constraint { public class UniqueKey extends Constraint {
public String sqlConstraintString(Dialect dialect) {
StringBuilder buf = new StringBuilder( "unique (" );
boolean hadNullableColumn = false;
Iterator iter = getColumnIterator();
while ( iter.hasNext() ) {
Column column = (Column) iter.next();
if ( !hadNullableColumn && column.isNullable() ) {
hadNullableColumn = true;
}
buf.append( column.getQuotedName( dialect ) );
if ( iter.hasNext() ) {
buf.append( ", " );
}
}
//do not add unique constraint on DB not supporting unique and nullable columns
return !hadNullableColumn || dialect.supportsNotNullUnique() ?
buf.append( ')' ).toString() :
null;
}
@Override @Override
public String sqlConstraintString( public String sqlConstraintString(
Dialect dialect, Dialect dialect,
String constraintName, String constraintName,
String defaultCatalog, String defaultCatalog,
String defaultSchema) { String defaultSchema) {
// return dialect.getUniqueDelegate().uniqueConstraintSql( this ); StringBuilder buf = new StringBuilder(
// Not used. dialect.getAddUniqueConstraintString( constraintName )
return ""; ).append( '(' );
Iterator iter = getColumnIterator();
boolean nullable = false;
while ( iter.hasNext() ) {
Column column = (Column) iter.next();
if ( !nullable && column.isNullable() ) nullable = true;
buf.append( column.getQuotedName( dialect ) );
if ( iter.hasNext() ) buf.append( ", " );
}
return !nullable || dialect.supportsNotNullUnique() ? buf.append( ')' ).toString() : null;
} }
@Override @Override
public String sqlCreateString(Dialect dialect, Mapping p, public String sqlCreateString(Dialect dialect, Mapping p, String defaultCatalog, String defaultSchema) {
String defaultCatalog, String defaultSchema) { if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
return dialect.getUniqueDelegate().applyUniquesOnAlter( return super.sqlCreateString( dialect, p, defaultCatalog, defaultSchema );
this, defaultCatalog, defaultSchema ); }
else {
return Index.buildSqlCreateIndexString( dialect, getName(), getTable(), getColumnIterator(), true,
defaultCatalog, defaultSchema );
}
} }
@Override @Override
public String sqlDropString(Dialect dialect, String defaultCatalog, public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) {
String defaultSchema) { if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
return dialect.getUniqueDelegate().dropUniquesOnAlter( return super.sqlDropString( dialect, defaultCatalog, defaultSchema );
this, defaultCatalog, defaultSchema ); }
else {
return Index.buildSqlDropIndexString( dialect, getTable(), getName(), defaultCatalog, defaultSchema );
}
}
@Override
public boolean isGenerated(Dialect dialect) {
if ( dialect.supportsNotNullUnique() ) return true;
Iterator iter = getColumnIterator();
while ( iter.hasNext() ) {
if ( ( (Column) iter.next() ).isNullable() ) {
return false;
}
}
return true;
} }
} }

View File

@ -115,8 +115,10 @@ public class Database {
for ( Schema schema : schemaMap.values() ) { for ( Schema schema : schemaMap.values() ) {
for ( Table table : schema.getTables() ) { for ( Table table : schema.getTables() ) {
for ( UniqueKey uniqueKey : table.getUniqueKeys() ) { if ( ! dialect.supportsUniqueConstraintInCreateAlterTable() ) {
addSqlCreateStrings( dialect, exportIdentifiers, script, uniqueKey ); for ( UniqueKey uniqueKey : table.getUniqueKeys() ) {
addSqlCreateStrings( dialect, exportIdentifiers, script, uniqueKey );
}
} }
for ( Index index : table.getIndexes() ) { for ( Index index : table.getIndexes() ) {

View File

@ -63,6 +63,7 @@ public class Index extends AbstractConstraint implements Constraint {
Iterable<Column> columns, Iterable<Column> columns,
boolean unique boolean unique
) { ) {
//TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far
StringBuilder buf = new StringBuilder( "create" ) StringBuilder buf = new StringBuilder( "create" )
.append( unique ? .append( unique ?
" unique" : " unique" :
@ -88,18 +89,6 @@ public class Index extends AbstractConstraint implements Constraint {
return buf.toString(); 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) { public String sqlConstraintStringInAlterTable(Dialect dialect) {
StringBuilder buf = new StringBuilder( " index (" ); StringBuilder buf = new StringBuilder( " index (" );
boolean first = true; boolean first = true;

View File

@ -199,12 +199,16 @@ public class Table extends AbstractTableSpecification implements Exportable {
} }
if ( col.isUnique() ) { boolean useUniqueConstraint = col.isUnique() &&
UniqueKey uk = getOrCreateUniqueKey( col.getColumnName() ( col.isNullable() || dialect.supportsNotNullUnique() );
.encloseInQuotesIfQuoted( dialect ) + '_' ); if ( useUniqueConstraint ) {
uk.addColumn( col ); if ( dialect.supportsUnique() ) {
buf.append( dialect.getUniqueDelegate() buf.append( " unique" );
.applyUniqueToColumn( col ) ); }
else {
UniqueKey uk = getOrCreateUniqueKey( col.getColumnName().encloseInQuotesIfQuoted( dialect ) + '_' );
uk.addColumn( col );
}
} }
if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) { if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) {
@ -223,7 +227,14 @@ public class Table extends AbstractTableSpecification implements Exportable {
.append( getPrimaryKey().sqlConstraintStringInCreateTable( dialect ) ); .append( getPrimaryKey().sqlConstraintStringInCreateTable( dialect ) );
} }
buf.append( dialect.getUniqueDelegate().applyUniquesToTable( this ) ); if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) {
for ( UniqueKey uk : uniqueKeys.values() ) {
String constraint = uk.sqlConstraintStringInCreateTable( dialect );
if ( constraint != null ) {
buf.append( ", " ).append( constraint );
}
}
}
if ( dialect.supportsTableCheck() ) { if ( dialect.supportsTableCheck() ) {
for ( CheckConstraint checkConstraint : checkConstraints ) { for ( CheckConstraint checkConstraint : checkConstraints ) {

View File

@ -48,20 +48,60 @@ public class UniqueKey extends AbstractConstraint implements Constraint {
} }
@Override @Override
public String[] sqlCreateStrings(Dialect dialect) { public boolean isCreationVetoed(Dialect dialect) {
String s = dialect.getUniqueDelegate().applyUniquesOnAlter(this); if ( dialect.supportsNotNullUnique() ) {
return StringHelper.toArrayElement( s ); return false;
}
for ( Column column : getColumns() ) {
if ( column.isNullable() ) {
return true;
}
}
return false;
}
public String sqlConstraintStringInCreateTable(Dialect dialect) {
StringBuilder buf = new StringBuilder( "unique (" );
boolean hadNullableColumn = false;
boolean first = true;
for ( Column column : getColumns() ) {
if ( first ) {
first = false;
}
else {
buf.append( ", " );
}
if ( !hadNullableColumn && column.isNullable() ) {
hadNullableColumn = true;
}
buf.append( column.getColumnName().encloseInQuotesIfQuoted( dialect ) );
}
//do not add unique constraint on DB not supporting unique and nullable columns
return !hadNullableColumn || dialect.supportsNotNullUnique() ?
buf.append( ')' ).toString() :
null;
} }
@Override @Override
public String[] sqlDropStrings(Dialect dialect) { public String sqlConstraintStringInAlterTable(Dialect dialect) {
String s = dialect.getUniqueDelegate().dropUniquesOnAlter(this); StringBuilder buf = new StringBuilder(
return StringHelper.toArrayElement( s ); dialect.getAddUniqueConstraintString( getName() )
} ).append( '(' );
boolean nullable = false;
@Override boolean first = true;
protected String sqlConstraintStringInAlterTable(Dialect dialect) { for ( Column column : getColumns() ) {
// not used if ( first ) {
return ""; first = false;
}
else {
buf.append( ", " );
}
if ( !nullable && column.isNullable() ) {
nullable = true;
}
buf.append( column.getColumnName().encloseInQuotesIfQuoted( dialect ) );
}
return !nullable || dialect.supportsNotNullUnique() ? buf.append( ')' ).toString() : null;
} }
} }

View File

@ -15,6 +15,8 @@ import org.hibernate.JDBCException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.UniqueKey;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test; import org.junit.Test;
@ -35,6 +37,7 @@ public class UniqueConstraintTest extends BaseCoreFunctionalTestCase {
} }
@Test @Test
@RequiresDialectFeature( DialectChecks.SupportNotNullUnique.class )
public void testUniquenessConstraintWithSuperclassProperty() throws Exception { public void testUniquenessConstraintWithSuperclassProperty() throws Exception {
Session s = openSession(); Session s = openSession();
Transaction tx = s.beginTransaction(); Transaction tx = s.beginTransaction();
@ -72,7 +75,7 @@ public class UniqueConstraintTest extends BaseCoreFunctionalTestCase {
Iterator<org.hibernate.mapping.Table> iterator = configuration().getTableMappings(); Iterator<org.hibernate.mapping.Table> iterator = configuration().getTableMappings();
org.hibernate.mapping.Table tableA = null; org.hibernate.mapping.Table tableA = null;
org.hibernate.mapping.Table tableB = null; org.hibernate.mapping.Table tableB = null;
while( iterator.hasNext() ) { while ( iterator.hasNext() ) {
org.hibernate.mapping.Table table = iterator.next(); org.hibernate.mapping.Table table = iterator.next();
if ( table.getName().equals( "UniqueNoNameA" ) ) { if ( table.getName().equals( "UniqueNoNameA" ) ) {
tableA = table; tableA = table;
@ -81,14 +84,13 @@ public class UniqueConstraintTest extends BaseCoreFunctionalTestCase {
tableB = table; tableB = table;
} }
} }
if ( tableA == null || tableB == null ) { if ( tableA == null || tableB == null ) {
fail( "Could not find the expected tables." ); fail( "Could not find the expected tables." );
} }
UniqueKey ukA = (UniqueKey) tableA.getUniqueKeyIterator().next(); assertFalse( ( (UniqueKey) tableA.getUniqueKeyIterator().next() ).getName().equals(
UniqueKey ukB = (UniqueKey) tableB.getUniqueKeyIterator().next(); ( (UniqueKey) tableB.getUniqueKeyIterator().next() ).getName() ) );
assertFalse( ukA.getName().equals( ukB.getName() ) );
} }
@Entity @Entity

View File

@ -1,85 +0,0 @@
/*
* 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.test.constraint;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.mapping.Column;
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 {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Entity1.class
};
}
@Test
public void testConstraints() {
Column column = (Column) configuration().getClassMapping( Entity1.class.getName() )
.getProperty( "foo1" ).getColumnIterator().next();
assertFalse( column.isNullable() );
assertTrue( column.isUnique() );
column = (Column) configuration().getClassMapping( Entity1.class.getName() )
.getProperty( "foo2" ).getColumnIterator().next();
assertTrue( column.isNullable() );
assertTrue( column.isUnique() );
column = (Column) configuration().getClassMapping( Entity1.class.getName() )
.getProperty( "id" ).getColumnIterator().next();
assertFalse( column.isNullable() );
assertTrue( column.isUnique() );
}
@Entity
@Table( name = "Entity1" )
public static class Entity1 {
@Id
@GeneratedValue
@javax.persistence.Column( nullable = false, unique = true)
public long id;
@javax.persistence.Column( nullable = false, unique = true)
public String foo1;
@javax.persistence.Column( nullable = true, unique = true)
public String foo2;
}
}

View File

@ -92,6 +92,12 @@ abstract public class DialectChecks {
} }
} }
public static class SupportNotNullUnique implements DialectCheck {
public boolean isMatch(Dialect dialect) {
return dialect.supportsNotNullUnique();
}
}
public static class SupportLimitCheck implements DialectCheck { public static class SupportLimitCheck implements DialectCheck {
public boolean isMatch(Dialect dialect) { public boolean isMatch(Dialect dialect) {
return dialect.supportsLimit(); return dialect.supportsLimit();