add some comments for the next poor soul who wrestles with unique constraints
This commit is contained in:
parent
5172d8798f
commit
12aa8bd431
|
@ -212,7 +212,9 @@ public class DB2LegacyDialect extends Dialect {
|
|||
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
return getDB2Version().isSameOrAfter(10,5)
|
||||
//use 'create unique index ... exclude null keys'
|
||||
? new AlterTableUniqueIndexDelegate( this )
|
||||
//ignore unique keys on nullable columns in earlier versions
|
||||
: new SkipNullableUniqueDelegate( this );
|
||||
}
|
||||
|
||||
|
@ -495,6 +497,8 @@ public class DB2LegacyDialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? " exclude null keys" : "";
|
||||
}
|
||||
|
||||
|
|
|
@ -73,8 +73,11 @@ public class DB2iLegacyDialect extends DB2LegacyDialect {
|
|||
|
||||
@Override
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
//TODO: when was 'create unique where not null index' really first introduced?
|
||||
return getVersion().isSameOrAfter(7, 1)
|
||||
//use 'create unique where not null index'
|
||||
? new AlterTableUniqueIndexDelegate(this)
|
||||
//ignore unique keys on nullable columns in earlier versions
|
||||
: new SkipNullableUniqueDelegate(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,8 +88,11 @@ public class DB2zLegacyDialect extends DB2LegacyDialect {
|
|||
|
||||
@Override
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
//TODO: when was 'create unique where not null index' really first introduced?
|
||||
return getVersion().isSameOrAfter(11)
|
||||
//use 'create unique where not null index'
|
||||
? new AlterTableUniqueIndexDelegate(this)
|
||||
//ignore unique keys on nullable columns in earlier versions
|
||||
: new SkipNullableUniqueDelegate(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,9 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
private UniqueDelegate createUniqueDelgate(DatabaseVersion version) {
|
||||
return version.isSameOrAfter(10)
|
||||
//use 'create unique nonclustered index ... where ...'
|
||||
? new AlterTableUniqueIndexDelegate(this)
|
||||
//ignore unique keys on nullable columns in versions before 2008
|
||||
: new SkipNullableUniqueDelegate(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,17 +75,16 @@ public class AlterTableUniqueDelegate implements UniqueDelegate {
|
|||
public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
|
||||
SqlStringGenerationContext context) {
|
||||
final String tableName = context.format( uniqueKey.getTable().getQualifiedTableName() );
|
||||
|
||||
final StringBuilder buf = new StringBuilder( dialect.getAlterTableString(tableName) );
|
||||
buf.append( dialect.getDropUniqueKeyString() );
|
||||
final StringBuilder command = new StringBuilder( dialect.getAlterTableString(tableName) );
|
||||
command.append( dialect.getDropUniqueKeyString() );
|
||||
if ( dialect.supportsIfExistsBeforeConstraintName() ) {
|
||||
buf.append( "if exists " );
|
||||
command.append( "if exists " );
|
||||
}
|
||||
buf.append( dialect.quote( uniqueKey.getName() ) );
|
||||
command.append( dialect.quote( uniqueKey.getName() ) );
|
||||
if ( dialect.supportsIfExistsAfterConstraintName() ) {
|
||||
buf.append( " if exists" );
|
||||
command.append( " if exists" );
|
||||
}
|
||||
return buf.toString();
|
||||
return command.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,15 +12,17 @@ import org.hibernate.dialect.Dialect;
|
|||
import org.hibernate.mapping.UniqueKey;
|
||||
|
||||
import static org.hibernate.mapping.Index.buildSqlCreateIndexString;
|
||||
import static org.hibernate.mapping.Index.buildSqlDropIndexString;
|
||||
|
||||
/**
|
||||
* A {@link UniqueDelegate} which uses {@code create unique index} commands when necessary.
|
||||
* <ul>
|
||||
* <li>DB2 does not allow unique constraints on nullable columns, but it does allow the creation
|
||||
* of unique indexes instead, using {@code create unique index ... exclude null keys}.
|
||||
* of unique indexes instead, using {@code create unique index ... exclude null keys} or
|
||||
* {@code create unique where not null index}, depending on flavor.
|
||||
* <li>SQL Server <em>does</em> allow unique constraints on nullable columns, but the semantics
|
||||
* are that two null values are non-unique. So here we need to jump through hoops with the
|
||||
* {@code create unique nonclustered index} command.
|
||||
* {@code create unique nonclustered index ... where ...} command.
|
||||
* </ul>
|
||||
*
|
||||
* @author Brett Meyer
|
||||
|
@ -53,7 +55,7 @@ public class AlterTableUniqueIndexDelegate extends AlterTableUniqueDelegate {
|
|||
public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
|
||||
SqlStringGenerationContext context) {
|
||||
if ( uniqueKey.hasNullableColumn() ) {
|
||||
return org.hibernate.mapping.Index.buildSqlDropIndexString(
|
||||
return buildSqlDropIndexString(
|
||||
uniqueKey.getName(),
|
||||
context.format( uniqueKey.getTable().getQualifiedTableName() )
|
||||
);
|
||||
|
|
|
@ -24,6 +24,9 @@ import org.hibernate.mapping.UniqueKey;
|
|||
* <li>For unique keys with no explicit name, it results in {@code unique(x, y)} after the
|
||||
* column list.
|
||||
* </ul>
|
||||
* Counterintuitively, this class extends {@link AlterTableUniqueDelegate}, since it falls back
|
||||
* to using {@code alter table} for {@linkplain org.hibernate.tool.schema.spi.SchemaMigrator
|
||||
* schema migration}.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
|
|
@ -19,7 +19,7 @@ import org.hibernate.mapping.UniqueKey;
|
|||
* Needed because unique constraints on nullable columns in Sybase always consider null values to be non-unique.
|
||||
* There is simply no way to create a unique constraint with the semantics we want on a nullable column in Sybase >:-(
|
||||
* <p>
|
||||
* You might argue that this was a bad decision because if the programmer explicitly specifies an {@code @UniqueKey},
|
||||
* You might argue that this behavior is bad because if the programmer explicitly specifies an {@code @UniqueKey},
|
||||
* then we should damn well respect their wishes. But the simple answer is that the user should have also specified
|
||||
* {@code @Column(nullable=false)} if that is what they wanted. A unique key on a nullable column just really doesn't
|
||||
* make sense in Sybase, except, perhaps, in some incredibly corner cases.
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.hibernate.mapping.Table;
|
|||
import org.hibernate.mapping.UniqueKey;
|
||||
|
||||
/**
|
||||
* Dialect-level delegate in charge of applying unique constraints in DDL. Uniqueness can
|
||||
* Dialect-level delegate responsible for applying unique constraints in DDL. Uniqueness can
|
||||
* be specified in any of three ways:
|
||||
* <ol>
|
||||
* <li>
|
||||
|
@ -30,7 +30,18 @@ import org.hibernate.mapping.UniqueKey;
|
|||
* Also, see {@link #getAlterTableToDropUniqueKeyCommand}.
|
||||
* </li>
|
||||
* </ol>
|
||||
* The first two options are generally preferred.
|
||||
* The first two options are generally preferred, and so we use {@link CreateTableUniqueDelegate}
|
||||
* where possible. However, for databases where unique constraints may not contain a nullable
|
||||
* column, and unique indexes must be used instead, we use {@link AlterTableUniqueIndexDelegate}.
|
||||
* <p>
|
||||
* Hibernate specifies that a unique constraint on a nullable column considers null values to be
|
||||
* distinct. Some databases default to the opposite semantic, where null values are considered
|
||||
* equal for the purpose of determining uniqueness. This is almost never useful, and is the
|
||||
* opposite of what we want when we use a unique constraint on a foreign key to map an optional
|
||||
* {@link org.hibernate.mapping.OneToOne} association. Therefore, our {@code UniqueDelegate}s must
|
||||
* jump through hoops to emulate the sensible semantics specified by ANSI, Hibernate, and common
|
||||
* sense, namely, that two null values are distinct. A particularly egregious offender is Sybase,
|
||||
* where we must simply {@linkplain SkipNullableUniqueDelegate skip creating the unique constraint}.
|
||||
*
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue