HHH-17103 allow SQL expressions in @Index(columnList)

This commit is contained in:
Gavin King 2023-08-17 20:55:06 +02:00
parent f1fa09ab42
commit 24646ece2c
13 changed files with 317 additions and 280 deletions

View File

@ -48,7 +48,7 @@ import org.hibernate.boot.model.internal.CreateKeySecondPass;
import org.hibernate.boot.model.internal.FkSecondPass; import org.hibernate.boot.model.internal.FkSecondPass;
import org.hibernate.boot.model.internal.IdGeneratorResolverSecondPass; import org.hibernate.boot.model.internal.IdGeneratorResolverSecondPass;
import org.hibernate.boot.model.internal.ImplicitToOneJoinTableSecondPass; import org.hibernate.boot.model.internal.ImplicitToOneJoinTableSecondPass;
import org.hibernate.boot.model.internal.JPAIndexHolder; import org.hibernate.boot.model.internal.IndexHolder;
import org.hibernate.boot.model.internal.OptionalDeterminationSecondPass; import org.hibernate.boot.model.internal.OptionalDeterminationSecondPass;
import org.hibernate.boot.model.internal.QuerySecondPass; import org.hibernate.boot.model.internal.QuerySecondPass;
import org.hibernate.boot.model.internal.SecondaryTableFromAnnotationSecondPass; import org.hibernate.boot.model.internal.SecondaryTableFromAnnotationSecondPass;
@ -58,6 +58,7 @@ import org.hibernate.boot.model.internal.UniqueConstraintHolder;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitForeignKeyNameSource; import org.hibernate.boot.model.naming.ImplicitForeignKeyNameSource;
import org.hibernate.boot.model.naming.ImplicitIndexNameSource; import org.hibernate.boot.model.naming.ImplicitIndexNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitUniqueKeyNameSource; import org.hibernate.boot.model.naming.ImplicitUniqueKeyNameSource;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.Database;
@ -91,6 +92,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.DenormalizedTable; import org.hibernate.mapping.DenormalizedTable;
import org.hibernate.mapping.FetchProfile; import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.IdentifierCollection; import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.Index; import org.hibernate.mapping.Index;
import org.hibernate.mapping.Join; import org.hibernate.mapping.Join;
@ -99,6 +101,7 @@ import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass; import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.UniqueKey;
@ -119,6 +122,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.MapsId; import jakarta.persistence.MapsId;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
/** /**
@ -178,7 +182,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private Map<String, String> propertyRefResolver; private Map<String, String> propertyRefResolver;
private Set<DelayedPropertyReferenceHandler> delayedPropertyReferenceHandlers; private Set<DelayedPropertyReferenceHandler> delayedPropertyReferenceHandlers;
private Map<Table, List<UniqueConstraintHolder>> uniqueConstraintHoldersByTable; private Map<Table, List<UniqueConstraintHolder>> uniqueConstraintHoldersByTable;
private Map<Table, List<JPAIndexHolder>> jpaIndexHoldersByTable; private Map<Table, List<IndexHolder>> indexHoldersByTable;
private List<Function<MetadataBuildingContext, Boolean>> valueResolvers; private List<Function<MetadataBuildingContext, Boolean>> valueResolvers;
public InFlightMetadataCollectorImpl( public InFlightMetadataCollectorImpl(
@ -1442,7 +1446,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
for ( String[] columns : uniqueConstraints ) { for ( String[] columns : uniqueConstraints ) {
final String keyName = "key" + keyNameBase++; final String keyName = "key" + keyNameBase++;
constraintHolders.add( constraintHolders.add(
new UniqueConstraintHolder().setName( keyName ).setColumns( columns ) new UniqueConstraintHolder( keyName, columns )
); );
} }
addUniqueConstraintHolders( table, constraintHolders ); addUniqueConstraintHolders( table, constraintHolders );
@ -1454,7 +1458,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
} }
@Override @Override
public void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> uniqueConstraintHolders) { public void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> holders) {
List<UniqueConstraintHolder> holderList = null; List<UniqueConstraintHolder> holderList = null;
if ( uniqueConstraintHoldersByTable == null ) { if ( uniqueConstraintHoldersByTable == null ) {
@ -1469,23 +1473,23 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
uniqueConstraintHoldersByTable.put( table, holderList ); uniqueConstraintHoldersByTable.put( table, holderList );
} }
holderList.addAll( uniqueConstraintHolders ); holderList.addAll( holders );
} }
@Override @Override
public void addJpaIndexHolders(Table table, List<JPAIndexHolder> holders) { public void addIndexHolders(Table table, List<IndexHolder> holders) {
List<JPAIndexHolder> holderList = null; List<IndexHolder> holderList = null;
if ( jpaIndexHoldersByTable == null ) { if ( indexHoldersByTable == null ) {
jpaIndexHoldersByTable = new HashMap<>(); indexHoldersByTable = new HashMap<>();
} }
else { else {
holderList = jpaIndexHoldersByTable.get( table ); holderList = indexHoldersByTable.get( table );
} }
if ( holderList == null ) { if ( holderList == null ) {
holderList = new ArrayList<>(); holderList = new ArrayList<>();
jpaIndexHoldersByTable.put( table, holderList ); indexHoldersByTable.put( table, holderList );
} }
holderList.addAll( holders ); holderList.addAll( holders );
@ -1845,7 +1849,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
secondPassCompileForeignKeys( buildingContext ); secondPassCompileForeignKeys( buildingContext );
processUniqueConstraintHolders( buildingContext ); processUniqueConstraintHolders( buildingContext );
processJPAIndexHolders( buildingContext ); processIndexHolders( buildingContext );
processNaturalIdUniqueKeyBinders(); processNaturalIdUniqueKeyBinders();
@ -2099,8 +2103,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
String keyName, String keyName,
boolean nameExplicit, boolean nameExplicit,
String[] columnNames, String[] columnNames,
MetadataBuildingContext buildingContext) { MetadataBuildingContext context) {
buildUniqueKeyFromColumnNames( table, keyName, nameExplicit, columnNames, null, true, buildingContext ); buildUniqueKeyFromColumnNames( table, keyName, nameExplicit, columnNames, null, true, context );
} }
private void buildUniqueKeyFromColumnNames( private void buildUniqueKeyFromColumnNames(
@ -2110,38 +2114,49 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
final String[] columnNames, final String[] columnNames,
String[] orderings, String[] orderings,
boolean unique, boolean unique,
final MetadataBuildingContext buildingContext) { final MetadataBuildingContext context) {
int size = columnNames.length; final int size = columnNames.length;
Column[] columns = new Column[size]; if ( size == 0 ) {
Set<Column> unbound = new HashSet<>(); throw new AnnotationException( ( unique ? "Unique constraint" : "Index" )
Set<Column> unboundNoLogical = new HashSet<>(); + ( isEmpty( keyName ) ? "" : " '" + keyName + "'" )
+ " on table '" + table.getName() + "' has no columns" );
}
final Selectable[] columns = new Selectable[size];
for ( int index = 0; index < size; index++ ) { for ( int index = 0; index < size; index++ ) {
final String logicalColumnName = columnNames[index]; final String columnName = columnNames[index];
if ( isEmpty( columnName ) ) {
throw new AnnotationException( ( unique ? "Unique constraint" : "Index" )
+ ( isEmpty( keyName ) ? "" : " '" + keyName + "'" )
+ " on table '" + table.getName() + "' has an empty column name" );
}
columns[index] = indexColumn( table, context, columnName);
}
createIndexOrUniqueKey( table, keyName, nameExplicit, columnNames, orderings, unique, context, columns );
}
private static Selectable indexColumn(Table table, MetadataBuildingContext buildingContext, String logicalColumnName) {
if ( logicalColumnName.startsWith("(") ) {
return new Formula( logicalColumnName );
}
else {
Column column;
try { try {
Column column = table.getColumn( buildingContext.getMetadataCollector(), logicalColumnName ); column = table.getColumn( buildingContext.getMetadataCollector(), logicalColumnName );
if ( column == null ) {
throw new AnnotationException(
"Table '" + table.getName() + "' has no column named '" + logicalColumnName
+ "' matching the column specified in '@UniqueConstraint'"
);
} }
columns[index] = column; catch (MappingException me) {
unbound.add( column ); column = null;
//column equals and hashcode is based on column name
} }
catch ( MappingException e ) { if ( column != null ) {
// If at least 1 columnName does exist, 'columns' will contain a mix of Columns and nulls. return column;
// In order to exhaustively report all the unbound columns at once, w/o an NPE in
// Constraint#generateName's array sorting, simply create a fake Column.
columns[index] = new Column( logicalColumnName );
unboundNoLogical.add( columns[index] );
} }
else {
// assume it's a SQL formula with missing parens
return new Formula( "(" + logicalColumnName + ")" );
// throw new AnnotationException(
// "Table '" + table.getName() + "' has no column named '" + logicalColumnName
// + "' matching the column specified in '@UniqueConstraint'"
// );
} }
createIndexOrUniqueKey( table, keyName, nameExplicit, columnNames, orderings, unique, buildingContext, columns, unbound );
if ( unbound.size() > 0 || unboundNoLogical.size() > 0 ) {
throwUnableToCreateConstraint( table, columnNames, unique, unbound, unboundNoLogical );
} }
} }
@ -2152,103 +2167,42 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
String[] columnNames, String[] columnNames,
String[] orderings, String[] orderings,
boolean unique, boolean unique,
MetadataBuildingContext buildingContext, MetadataBuildingContext context,
Column[] columns, Selectable[] columns) {
Set<Column> unbound) { final ImplicitNamingStrategy naming = getMetadataBuildingOptions().getImplicitNamingStrategy();
if (unique) { final IndexOrUniqueKeyNameSource source =
createUniqueKey( table, originalKeyName, nameExplicit, columnNames, orderings, buildingContext, columns, unbound ); new IndexOrUniqueKeyNameSource( context, table, columnNames, originalKeyName );
final Dialect dialect = getDatabase().getJdbcEnvironment().getDialect();
boolean hasFormula = false;
for ( Selectable selectable : columns ) {
if ( selectable.isFormula() ) {
hasFormula = true;
}
}
if ( unique && !hasFormula ) {
final String keyName = naming.determineUniqueKeyName( source ).render( dialect );
final UniqueKey uniqueKey = table.getOrCreateUniqueKey( keyName );
uniqueKey.setNameExplicit( nameExplicit );
for ( int i = 0; i < columns.length; i++ ) {
uniqueKey.addColumn( (Column) columns[i], orderings != null ? orderings[i] : null );
}
} }
else { else {
createIndex( table, originalKeyName, columnNames, orderings, buildingContext, columns, unbound ); final String keyName = naming.determineIndexName( source ).render( dialect );
}
}
private void createIndex(
Table table,
String originalKeyName,
String[] columnNames,
String[] orderings,
MetadataBuildingContext buildingContext,
Column[] columns,
Set<Column> unbound) {
final Identifier keyNameIdentifier = getMetadataBuildingOptions().getImplicitNamingStrategy()
.determineIndexName( new IndexOrUniqueKeyNameSource( buildingContext, table, columnNames, originalKeyName ) );
final String keyName = keyNameIdentifier.render( getDatabase().getJdbcEnvironment().getDialect() );
final Index index = table.getOrCreateIndex( keyName ); final Index index = table.getOrCreateIndex( keyName );
index.setUnique( unique );
for ( int i = 0; i < columns.length; i++ ) { for ( int i = 0; i < columns.length; i++ ) {
Column column = columns[i]; index.addColumn( columns[i], orderings != null ? orderings[i] : null );
String order = orderings != null ? orderings[i] : null;
if ( table.containsColumn( column ) ) {
index.addColumn( column, order );
unbound.remove( column );
} }
} }
} }
private void createUniqueKey( private void processIndexHolders(MetadataBuildingContext context) {
Table table, if ( indexHoldersByTable != null ) {
String originalKeyName, for ( Map.Entry<Table, List<IndexHolder>> entry : indexHoldersByTable.entrySet() ) {
boolean nameExplicit,
String[] columnNames,
String[] orderings,
MetadataBuildingContext buildingContext,
Column[] columns,
Set<Column> unbound) {
final Identifier keyNameIdentifier = getMetadataBuildingOptions().getImplicitNamingStrategy()
.determineUniqueKeyName( new IndexOrUniqueKeyNameSource( buildingContext, table, columnNames, originalKeyName ) );
final String keyName = keyNameIdentifier.render( getDatabase().getJdbcEnvironment().getDialect() );
final UniqueKey uk = table.getOrCreateUniqueKey( keyName );
uk.setNameExplicit(nameExplicit);
for (int i = 0; i < columns.length; i++ ) {
Column column = columns[i];
String order = orderings != null ? orderings[i] : null;
if ( table.containsColumn( column ) ) {
uk.addColumn( column, order );
unbound.remove( column );
}
}
}
private static void throwUnableToCreateConstraint(
Table table,
String[] columnNames,
boolean unique,
Set<Column> unbound,
Set<Column> unboundNoLogical) {
final StringBuilder message = new StringBuilder( "Unable to create " );
if (unique) {
message.append( "unique key constraint (" );
}
else {
message.append( "index (" );
}
for ( String columnName : columnNames) {
message.append( columnName ).append( ", " );
}
message.setLength( message.length() - 2 );
message.append( ") on table '" ).append( table.getName() ).append( "' since the column " );
for ( Column column : unbound) {
message.append("'").append( column.getName() ).append( "', " );
}
for ( Column column : unboundNoLogical) {
message.append("'").append( column.getName() ).append( "', " );
}
message.setLength( message.length() - 2 );
message.append( " was not found (specify the correct column name, which depends on the naming strategy, and may not be the same as the entity property name)" );
throw new AnnotationException( message.toString() );
}
private void processJPAIndexHolders(MetadataBuildingContext buildingContext) {
if ( jpaIndexHoldersByTable == null ) {
return;
}
for ( Map.Entry<Table, List<JPAIndexHolder>> entry : jpaIndexHoldersByTable.entrySet() ) {
final Table table = entry.getKey(); final Table table = entry.getKey();
final List<JPAIndexHolder> jpaIndexHolders = entry.getValue(); final List<IndexHolder> indexHolders = entry.getValue();
for ( JPAIndexHolder holder : jpaIndexHolders ) { for ( IndexHolder holder : indexHolders) {
buildUniqueKeyFromColumnNames( buildUniqueKeyFromColumnNames(
table, table,
holder.getName(), holder.getName(),
@ -2256,11 +2210,12 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
holder.getColumns(), holder.getColumns(),
holder.getOrdering(), holder.getOrdering(),
holder.isUnique(), holder.isUnique(),
buildingContext context
); );
} }
} }
} }
}
private Map<String,NaturalIdUniqueKeyBinder> naturalIdUniqueKeyBinderMap; private Map<String,NaturalIdUniqueKeyBinder> naturalIdUniqueKeyBinderMap;

View File

@ -644,21 +644,21 @@ public class EntityBinder {
final String schema; final String schema;
final String table; final String table;
final String catalog; final String catalog;
final List<UniqueConstraintHolder> uniqueConstraints; final UniqueConstraint[] uniqueConstraints;
boolean hasTableAnnotation = annotatedClass.isAnnotationPresent( jakarta.persistence.Table.class ); boolean hasTableAnnotation = annotatedClass.isAnnotationPresent( jakarta.persistence.Table.class );
if ( hasTableAnnotation ) { if ( hasTableAnnotation ) {
final jakarta.persistence.Table tableAnnotation = annotatedClass.getAnnotation( jakarta.persistence.Table.class ); final jakarta.persistence.Table tableAnnotation = annotatedClass.getAnnotation( jakarta.persistence.Table.class );
table = tableAnnotation.name(); table = tableAnnotation.name();
schema = tableAnnotation.schema(); schema = tableAnnotation.schema();
catalog = tableAnnotation.catalog(); catalog = tableAnnotation.catalog();
uniqueConstraints = TableBinder.buildUniqueConstraintHolders( tableAnnotation.uniqueConstraints() ); uniqueConstraints = tableAnnotation.uniqueConstraints();
} }
else { else {
//might be no @Table annotation on the annotated class //might be no @Table annotation on the annotated class
schema = ""; schema = "";
table = ""; table = "";
catalog = ""; catalog = "";
uniqueConstraints = Collections.emptyList(); uniqueConstraints = new UniqueConstraint[0];
} }
final InFlightMetadataCollector collector = context.getMetadataCollector(); final InFlightMetadataCollector collector = context.getMetadataCollector();
@ -684,7 +684,7 @@ public class EntityBinder {
String schema, String schema,
String table, String table,
String catalog, String catalog,
List<UniqueConstraintHolder> uniqueConstraints, UniqueConstraint[] uniqueConstraints,
InFlightMetadataCollector collector) { InFlightMetadataCollector collector) {
final RowId rowId = annotatedClass.getAnnotation( RowId.class ); final RowId rowId = annotatedClass.getAnnotation( RowId.class );
final View view = annotatedClass.getAnnotation( View.class ); final View view = annotatedClass.getAnnotation( View.class );
@ -1749,7 +1749,7 @@ public class EntityBinder {
String schema, String schema,
String catalog, String catalog,
String tableName, String tableName,
List<UniqueConstraintHolder> uniqueConstraints, UniqueConstraint[] uniqueConstraints,
String rowId, String rowId,
String viewQuery, String viewQuery,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) { InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
@ -2050,7 +2050,9 @@ public class EntityBinder {
secondaryTable.pkJoinColumns(), secondaryTable.pkJoinColumns(),
secondaryTable.uniqueConstraints() secondaryTable.uniqueConstraints()
); );
TableBinder.addIndexes( join.getTable(), secondaryTable.indexes(), context ); final Table table = join.getTable();
context.getMetadataCollector()
.addIndexHolders( table, TableBinder.buildIndexHolders( secondaryTable.indexes() ) );
return join; return join;
} }
@ -2083,7 +2085,7 @@ public class EntityBinder {
catalog, catalog,
logicalName.getTableName(), logicalName.getTableName(),
false, false,
TableBinder.buildUniqueConstraintHolders( uniqueConstraints ), uniqueConstraints,
context context
) )
); );
@ -2237,7 +2239,9 @@ public class EntityBinder {
public void processComplementaryTableDefinitions(jakarta.persistence.Table table) { public void processComplementaryTableDefinitions(jakarta.persistence.Table table) {
if ( table != null ) { if ( table != null ) {
TableBinder.addIndexes( persistentClass.getTable(), table.indexes(), context ); final Table classTable = persistentClass.getTable();
context.getMetadataCollector()
.addIndexHolders( classTable, TableBinder.buildIndexHolders( table.indexes() ) );
} }
} }

View File

@ -16,23 +16,26 @@ import jakarta.persistence.Index;
/** /**
* @author Strong Liu * @author Strong Liu
*/ */
public class JPAIndexHolder { public class IndexHolder {
private final String name; private final String name;
private final String[] columns; private final String[] columns;
private final String[] ordering; private final String[] ordering;
private final boolean unique; private final boolean unique;
public JPAIndexHolder(Index index) { public IndexHolder(Index index) {
StringTokenizer tokenizer = new StringTokenizer( index.columnList(), "," ); final StringTokenizer tokenizer = new StringTokenizer( index.columnList(), "," );
List<String> tmp = new ArrayList<>(); final List<String> parsed = new ArrayList<>();
while ( tokenizer.hasMoreElements() ) { while ( tokenizer.hasMoreElements() ) {
tmp.add( tokenizer.nextToken().trim() ); final String trimmed = tokenizer.nextToken().trim();
if ( !trimmed.isEmpty() ) {
parsed.add( trimmed ) ;
}
} }
this.name = index.name(); this.name = index.name();
this.columns = new String[tmp.size()]; this.columns = new String[parsed.size()];
this.ordering = new String[tmp.size()]; this.ordering = new String[parsed.size()];
this.unique = index.unique(); this.unique = index.unique();
initializeColumns( columns, ordering, tmp ); initializeColumns( columns, ordering, parsed );
} }
public String[] getColumns() { public String[] getColumns() {

View File

@ -65,7 +65,6 @@ public class TableBinder {
private String catalog; private String catalog;
private String name; private String name;
private boolean isAbstract; private boolean isAbstract;
private List<UniqueConstraintHolder> uniqueConstraints;
private String ownerEntityTable; private String ownerEntityTable;
private String associatedEntityTable; private String associatedEntityTable;
private String propertyName; private String propertyName;
@ -76,7 +75,8 @@ public class TableBinder {
private String associatedEntity; private String associatedEntity;
private String associatedJpaEntity; private String associatedJpaEntity;
private boolean isJPA2ElementCollection; private boolean isJPA2ElementCollection;
private List<JPAIndexHolder> jpaIndexHolders; private UniqueConstraint[] uniqueConstraints;
private Index[] indexes;
public void setBuildingContext(MetadataBuildingContext buildingContext) { public void setBuildingContext(MetadataBuildingContext buildingContext) {
this.buildingContext = buildingContext; this.buildingContext = buildingContext;
@ -103,11 +103,11 @@ public class TableBinder {
} }
public void setUniqueConstraints(UniqueConstraint[] uniqueConstraints) { public void setUniqueConstraints(UniqueConstraint[] uniqueConstraints) {
this.uniqueConstraints = TableBinder.buildUniqueConstraintHolders( uniqueConstraints ); this.uniqueConstraints = uniqueConstraints;
} }
public void setJpaIndex(Index[] jpaIndex){ public void setJpaIndex(Index[] indexes){
this.jpaIndexHolders = buildJpaIndexHolder( jpaIndex ); this.indexes = indexes;
} }
public void setJPA2ElementCollection(boolean isJPA2ElementCollection) { public void setJPA2ElementCollection(boolean isJPA2ElementCollection) {
@ -292,7 +292,7 @@ public class TableBinder {
: namingStrategyHelper.determineImplicitName( buildingContext ), : namingStrategyHelper.determineImplicitName( buildingContext ),
isAbstract, isAbstract,
uniqueConstraints, uniqueConstraints,
jpaIndexHolders, indexes,
buildingContext, buildingContext,
null, null,
null null
@ -434,7 +434,7 @@ public class TableBinder {
String catalog, String catalog,
Identifier logicalName, Identifier logicalName,
boolean isAbstract, boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints, UniqueConstraint[] uniqueConstraints,
MetadataBuildingContext buildingContext) { MetadataBuildingContext buildingContext) {
return buildAndFillTable( return buildAndFillTable(
schema, schema,
@ -454,7 +454,7 @@ public class TableBinder {
String catalog, String catalog,
Identifier logicalName, Identifier logicalName,
boolean isAbstract, boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints, UniqueConstraint[] uniqueConstraints,
MetadataBuildingContext buildingContext, MetadataBuildingContext buildingContext,
String subselect, String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) { InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
@ -476,8 +476,8 @@ public class TableBinder {
String catalog, String catalog,
Identifier logicalName, Identifier logicalName,
boolean isAbstract, boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints, UniqueConstraint[] uniqueConstraints,
List<JPAIndexHolder> jpaIndexHolders, Index[] indexes,
MetadataBuildingContext buildingContext, MetadataBuildingContext buildingContext,
String subselect, String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) { InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
@ -489,11 +489,11 @@ public class TableBinder {
denormalizedSuperTableXref, metadataCollector ); denormalizedSuperTableXref, metadataCollector );
if ( isNotEmpty( uniqueConstraints ) ) { if ( isNotEmpty( uniqueConstraints ) ) {
metadataCollector.addUniqueConstraintHolders( table, uniqueConstraints ); metadataCollector.addUniqueConstraintHolders( table, buildUniqueConstraintHolders( uniqueConstraints ) );
} }
if ( isNotEmpty( jpaIndexHolders ) ) { if ( isNotEmpty( indexes ) ) {
metadataCollector.addJpaIndexHolders( table, jpaIndexHolders ); metadataCollector.addIndexHolders( table, buildIndexHolders( indexes ) );
} }
metadataCollector.addTableNameBinding( logicalName, table ); metadataCollector.addTableNameBinding( logicalName, table );
@ -806,7 +806,7 @@ public class TableBinder {
} }
} }
public static void addIndexes(Table table, org.hibernate.annotations.Index[] indexes, MetadataBuildingContext context) { static void addIndexes(Table table, org.hibernate.annotations.Index[] indexes, MetadataBuildingContext context) {
for ( org.hibernate.annotations.Index index : indexes ) { for ( org.hibernate.annotations.Index index : indexes ) {
//no need to handle inSecondPass here since it is only called from EntityBinder //no need to handle inSecondPass here since it is only called from EntityBinder
context.getMetadataCollector().addSecondPass( context.getMetadataCollector().addSecondPass(
@ -815,39 +815,34 @@ public class TableBinder {
} }
} }
public static void addIndexes(Table table, Index[] indexes, MetadataBuildingContext context) { /**
context.getMetadataCollector().addJpaIndexHolders( table, buildJpaIndexHolder( indexes ) ); * Build a list of {@link IndexHolder} instances given a
} * list of {@link Index} annotations.
*/
public static List<JPAIndexHolder> buildJpaIndexHolder(Index[] indexes) { static List<IndexHolder> buildIndexHolders(Index[] indexes) {
List<JPAIndexHolder> holders = new ArrayList<>( indexes.length ); List<IndexHolder> holders = new ArrayList<>( indexes.length );
for ( Index index : indexes ) { for ( Index index : indexes ) {
holders.add( new JPAIndexHolder( index ) ); holders.add( new IndexHolder( index ) );
} }
return holders; return holders;
} }
/** /**
* Build a list of {@link UniqueConstraintHolder} instances given a list of * Build a list of {@link UniqueConstraintHolder} instances
* {@link UniqueConstraint} annotations. * given a list of {@link UniqueConstraint} annotations.
*
* @param annotations The {@link UniqueConstraint} annotations.
*
* @return The built {@link UniqueConstraintHolder} instances.
*/ */
public static List<UniqueConstraintHolder> buildUniqueConstraintHolders(UniqueConstraint[] annotations) { static List<UniqueConstraintHolder> buildUniqueConstraintHolders(UniqueConstraint[] uniqueConstraints) {
List<UniqueConstraintHolder> result; if ( uniqueConstraints == null || uniqueConstraints.length == 0 ) {
if ( annotations == null || annotations.length == 0 ) { return emptyList();
result = emptyList();
} }
else { else {
result = arrayList( annotations.length ); final List<UniqueConstraintHolder> result = arrayList( uniqueConstraints.length );
for ( UniqueConstraint uc : annotations ) { for ( UniqueConstraint uniqueConstraint : uniqueConstraints ) {
result.add( new UniqueConstraintHolder().setName( uc.name(), !uc.name().isEmpty() ).setColumns( uc.columnNames() ) ); result.add( new UniqueConstraintHolder( uniqueConstraint ) );
}
} }
return result; return result;
} }
}
public void setDefaultName( public void setDefaultName(
String ownerClassName, String ownerClassName,

View File

@ -7,28 +7,33 @@
package org.hibernate.boot.model.internal; package org.hibernate.boot.model.internal;
import jakarta.persistence.UniqueConstraint;
/** /**
* {@link jakarta.persistence.UniqueConstraint} annotations are handled via second pass. I do not * {@link jakarta.persistence.UniqueConstraint} annotations are handled via second pass. I do not
* understand the reasons why at this time, so here I use a holder object to hold the information * understand the reasons why at this time, so here I use a holder object to hold the information
* needed to create the unique constraint. The ability to name it is new, and so the code used to * needed to create the unique constraint. The ability to name it is new, and so the code used to
* simply keep this as a String array (the column names). * simply keep this as a String array (the column names).
* *
* Isn't this ultimately the same as org.hibernate.cfg.IndexOrUniqueKeySecondPass?
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
// Isn't this ultimately the same as IndexOrUniqueKeySecondPass?
public class UniqueConstraintHolder { public class UniqueConstraintHolder {
private String name; private final String name;
private boolean nameExplicit; private final String[] columns;
private String[] columns;
public String getName() { public String getName() {
return name; return name;
} }
public UniqueConstraintHolder setName(String name) { public UniqueConstraintHolder(UniqueConstraint uniqueConstraint) {
this.name = uniqueConstraint.name();
this.columns = uniqueConstraint.columnNames();
}
public UniqueConstraintHolder(String name, String[] columns) {
this.name = name; this.name = name;
return this; this.columns = columns;
} }
public boolean isNameExplicit() { public boolean isNameExplicit() {
@ -38,14 +43,4 @@ public class UniqueConstraintHolder {
public String[] getColumns() { public String[] getColumns() {
return columns; return columns;
} }
public UniqueConstraintHolder setColumns(String[] columns) {
this.columns = columns;
return this;
}
public UniqueConstraintHolder setName(String name, boolean nameExplicit) {
this.nameExplicit = nameExplicit;
return setName(name);
}
} }

View File

@ -27,7 +27,7 @@ import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterRegistry; import org.hibernate.boot.model.convert.spi.ConverterRegistry;
import org.hibernate.boot.model.convert.spi.RegisteredConversion; import org.hibernate.boot.model.convert.spi.RegisteredConversion;
import org.hibernate.boot.model.internal.AnnotatedClassType; import org.hibernate.boot.model.internal.AnnotatedClassType;
import org.hibernate.boot.model.internal.JPAIndexHolder; import org.hibernate.boot.model.internal.IndexHolder;
import org.hibernate.boot.model.internal.UniqueConstraintHolder; import org.hibernate.boot.model.internal.UniqueConstraintHolder;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
@ -359,7 +359,7 @@ public interface InFlightMetadataCollector extends MetadataImplementor {
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
void addUniqueConstraints(Table table, List<String[]> uniqueConstraints); void addUniqueConstraints(Table table, List<String[]> uniqueConstraints);
void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> uniqueConstraints); void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> uniqueConstraints);
void addJpaIndexHolders(Table table, List<JPAIndexHolder> jpaIndexHolders); void addIndexHolders(Table table, List<IndexHolder> indexHolders);
interface EntityTableXref { interface EntityTableXref {

View File

@ -9,10 +9,14 @@ package org.hibernate.dialect.unique;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.UniqueKey; import org.hibernate.mapping.UniqueKey;
import static org.hibernate.mapping.Index.buildSqlCreateIndexString; import java.util.List;
import static org.hibernate.mapping.Index.buildSqlDropIndexString; import java.util.Map;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.unqualify;
/** /**
* A {@link UniqueDelegate} which uses {@code create unique index} commands when necessary. * A {@link UniqueDelegate} which uses {@code create unique index} commands when necessary.
@ -36,15 +40,34 @@ public class AlterTableUniqueIndexDelegate extends AlterTableUniqueDelegate {
public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata, public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
SqlStringGenerationContext context) { SqlStringGenerationContext context) {
if ( uniqueKey.hasNullableColumn() ) { if ( uniqueKey.hasNullableColumn() ) {
return buildSqlCreateIndexString( final Dialect dialect = context.getDialect();
context, final String name = uniqueKey.getName();
uniqueKey.getName(), final String tableName = context.format( uniqueKey.getTable().getQualifiedTableName() );
uniqueKey.getTable(), final List<Column> columns = uniqueKey.getColumns();
uniqueKey.getColumns(), final Map<Column, String> columnOrderMap = uniqueKey.getColumnOrderMap();
uniqueKey.getColumnOrderMap(), final StringBuilder statement =
true, new StringBuilder( dialect.getCreateIndexString( true ) )
metadata .append( " " )
); .append( dialect.qualifyIndexName() ? name : unqualify( name ) )
.append( " on " )
.append( tableName )
.append( " (" );
boolean first = true;
for ( Column column : columns ) {
if ( first ) {
first = false;
}
else {
statement.append(", ");
}
statement.append( column.getQuotedName(dialect) );
if ( columnOrderMap.containsKey( column ) ) {
statement.append( " " ).append( columnOrderMap.get( column ) );
}
}
statement.append( ")" );
statement.append( dialect.getCreateIndexTail( true, columns ) );
return statement.toString();
} }
else { else {
return super.getAlterTableToAddUniqueKeyCommand( uniqueKey, metadata, context ); return super.getAlterTableToAddUniqueKeyCommand( uniqueKey, metadata, context );
@ -55,13 +78,12 @@ public class AlterTableUniqueIndexDelegate extends AlterTableUniqueDelegate {
public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata, public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
SqlStringGenerationContext context) { SqlStringGenerationContext context) {
if ( uniqueKey.hasNullableColumn() ) { if ( uniqueKey.hasNullableColumn() ) {
return buildSqlDropIndexString( final String tableName = context.format( uniqueKey.getTable().getQualifiedTableName() );
uniqueKey.getName(), return "drop index " + qualify( tableName, uniqueKey.getName() );
context.format( uniqueKey.getTable().getQualifiedTableName() )
);
} }
else { else {
return super.getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata, context ); return super.getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata, context );
} }
} }
} }

View File

@ -571,8 +571,8 @@ public class Column implements Selectable, Serializable, Cloneable, ColumnTypeIn
} }
@Override @Override
public String getText(Dialect d) { public String getText(Dialect dialect) {
return assignmentExpression != null ? assignmentExpression : getQuotedName( d ); return assignmentExpression != null ? assignmentExpression : getQuotedName( dialect );
} }
@Override @Override

View File

@ -12,7 +12,6 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;

View File

@ -10,7 +10,6 @@ import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.internal.AliasConstantsHelper; import org.hibernate.loader.internal.AliasConstantsHelper;
import org.hibernate.query.sqm.function.SqmFunctionRegistry; import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -95,4 +94,15 @@ public class Formula implements Selectable, Serializable {
public String toString() { public String toString() {
return getClass().getSimpleName() + "( " + formula + " )"; return getClass().getSimpleName() + "( " + formula + " )";
} }
@Override
public boolean equals(Object obj) {
return obj instanceof Formula
&& ( (Formula) obj ).formula.equals( formula );
}
@Override
public int hashCode() {
return formula.hashCode();
}
} }

View File

@ -9,19 +9,22 @@ package org.hibernate.mapping;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.List;
import java.util.Map;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Exportable; import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.StringHelper;
import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.toUnmodifiableList;
import static java.util.stream.Collectors.toUnmodifiableMap;
import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.internal.util.StringHelper.qualify; import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.unqualify;
/** /**
* A mapping model object representing an {@linkplain jakarta.persistence.Index index} on a relational database table. * A mapping model object representing an {@linkplain jakarta.persistence.Index index} on a relational database table.
@ -34,15 +37,22 @@ import static org.hibernate.internal.util.StringHelper.qualify;
public class Index implements Exportable, Serializable { public class Index implements Exportable, Serializable {
private Identifier name; private Identifier name;
private Table table; private Table table;
private final java.util.List<Column> columns = new ArrayList<>(); private boolean unique;
private final java.util.Map<Column, String> columnOrderMap = new HashMap<>( ); private final java.util.List<Selectable> selectables = new ArrayList<>();
private final java.util.Map<Selectable, String> selectableOrderMap = new HashMap<>();
public static String buildSqlDropIndexString( /**
String name, * @deprecated This method will be removed in the next release
String tableName) { */
@Deprecated(forRemoval = true)
public static String buildSqlDropIndexString(String name, String tableName) {
return "drop index " + qualify( tableName, name ); return "drop index " + qualify( tableName, name );
} }
/**
* @deprecated This method will be removed in the next release
*/
@Deprecated(forRemoval = true)
public static String buildSqlCreateIndexString( public static String buildSqlCreateIndexString(
Dialect dialect, Dialect dialect,
String name, String name,
@ -52,7 +62,7 @@ public class Index implements Exportable, Serializable {
boolean unique) { boolean unique) {
StringBuilder statement = new StringBuilder( dialect.getCreateIndexString( unique ) ) StringBuilder statement = new StringBuilder( dialect.getCreateIndexString( unique ) )
.append( " " ) .append( " " )
.append( dialect.qualifyIndexName() ? name : StringHelper.unqualify( name ) ) .append( dialect.qualifyIndexName() ? name : unqualify( name ) )
.append( " on " ) .append( " on " )
.append( tableName ) .append( tableName )
.append( " (" ); .append( " (" );
@ -75,6 +85,10 @@ public class Index implements Exportable, Serializable {
return statement.toString(); return statement.toString();
} }
/**
* @deprecated This method will be removed in the next release
*/
@Deprecated(forRemoval = true)
public static String buildSqlCreateIndexString( public static String buildSqlCreateIndexString(
SqlStringGenerationContext context, SqlStringGenerationContext context,
String name, String name,
@ -101,39 +115,73 @@ public class Index implements Exportable, Serializable {
this.table = table; this.table = table;
} }
public void setUnique(boolean unique) {
this.unique = unique;
}
public boolean isUnique() {
return unique;
}
public int getColumnSpan() { public int getColumnSpan() {
return columns.size(); return selectables.size();
} }
public List<Selectable> getSelectables() {
return unmodifiableList( selectables );
}
public Map<Selectable, String> getSelectableOrderMap() {
return unmodifiableMap( selectableOrderMap );
}
/**
* @deprecated use {@link #getSelectables()}
*/
@Deprecated(since = "6.3")
public java.util.List<Column> getColumns() { public java.util.List<Column> getColumns() {
return unmodifiableList( columns ); return selectables.stream()
.map( s -> (Column) s ).collect( toUnmodifiableList() );
} }
/**
* @deprecated use {@link #getSelectableOrderMap()}
*/
@Deprecated(since = "6.3")
public java.util.Map<Column, String> getColumnOrderMap() { public java.util.Map<Column, String> getColumnOrderMap() {
return unmodifiableMap( columnOrderMap ); return selectableOrderMap.entrySet().stream()
.collect( toUnmodifiableMap( e -> (Column) e.getKey(), Map.Entry::getValue ) );
} }
public void addColumn(Column column) { public void addColumn(Selectable selectable) {
if ( !columns.contains( column ) ) { if ( !selectables.contains( selectable ) ) {
columns.add( column ); selectables.add( selectable );
} }
} }
public void addColumn(Column column, String order) { public void addColumn(Selectable selectable, String order) {
addColumn( column ); addColumn( selectable );
if ( isNotEmpty( order ) ) { if ( isNotEmpty( order ) ) {
columnOrderMap.put( column, order ); selectableOrderMap.put( selectable, order );
} }
} }
/**
* @deprecated use {@link #getSelectableOrderMap()}
*/
@Deprecated(since = "6.3", forRemoval = true)
public void addColumns(java.util.List<Column> extraColumns) { public void addColumns(java.util.List<Column> extraColumns) {
for ( Column column : extraColumns ) { for ( Column column : extraColumns ) {
addColumn( column ); addColumn( column );
} }
} }
/**
* @deprecated use {@link #getSelectableOrderMap()}
*/
@Deprecated(since = "6.3", forRemoval = true)
public boolean containsColumn(Column column) { public boolean containsColumn(Column column) {
return columns.contains( column ); return selectables.contains( column );
} }
public String getName() { public String getName() {

View File

@ -10,14 +10,15 @@ import java.util.Map;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.relational.QualifiedNameImpl; import org.hibernate.boot.model.relational.QualifiedNameImpl;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Index; import org.hibernate.mapping.Index;
import org.hibernate.mapping.Selectable;
import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.tool.schema.spi.Exporter;
import static org.hibernate.internal.util.StringHelper.qualify;
/** /**
* An {@link Exporter} for {@linkplain Index indexes}. * An {@link Exporter} for {@linkplain Index indexes}.
* *
@ -37,45 +38,50 @@ public class StandardIndexExporter implements Exporter<Index> {
@Override @Override
public String[] getSqlCreateStrings(Index index, Metadata metadata, SqlStringGenerationContext context) { public String[] getSqlCreateStrings(Index index, Metadata metadata, SqlStringGenerationContext context) {
final JdbcEnvironment jdbcEnvironment = metadata.getDatabase().getJdbcEnvironment(); final StringBuilder createIndex = new StringBuilder()
final String tableName = context.format( index.getTable().getQualifiedTableName() ); .append( dialect.getCreateIndexString( index.isUnique() ) )
.append( " " )
.append( indexName( index, context, metadata ) )
.append( " on " )
.append( context.format( index.getTable().getQualifiedTableName() ) )
.append( " (" );
appendColumnList( index, createIndex );
createIndex.append( ")" );
return new String[] { createIndex.toString() };
}
final String indexNameForCreation; private String indexName(Index index, SqlStringGenerationContext context, Metadata metadata) {
if ( dialect.qualifyIndexName() ) { if ( dialect.qualifyIndexName() ) {
indexNameForCreation = context.format( final QualifiedTableName qualifiedTableName = index.getTable().getQualifiedTableName();
return context.format(
new QualifiedNameImpl( new QualifiedNameImpl(
index.getTable().getQualifiedTableName().getCatalogName(), qualifiedTableName.getCatalogName(),
index.getTable().getQualifiedTableName().getSchemaName(), qualifiedTableName.getSchemaName(),
jdbcEnvironment.getIdentifierHelper().toIdentifier( index.getQuotedName( dialect ) ) metadata.getDatabase().getJdbcEnvironment().getIdentifierHelper()
.toIdentifier( index.getQuotedName( dialect ) )
) )
); );
} }
else { else {
indexNameForCreation = index.getName(); return index.getName();
}
} }
final StringBuilder buf = new StringBuilder()
.append( "create index " )
.append( indexNameForCreation )
.append( " on " )
.append( tableName )
.append( " (" );
private void appendColumnList(Index index, StringBuilder createIndex) {
boolean first = true; boolean first = true;
final Map<Column, String> columnOrderMap = index.getColumnOrderMap(); final Map<Selectable, String> columnOrderMap = index.getSelectableOrderMap();
for ( Column column : index.getColumns() ) { for ( Selectable column : index.getSelectables() ) {
if ( first ) { if ( first ) {
first = false; first = false;
} }
else { else {
buf.append( ", " ); createIndex.append( ", " );
} }
buf.append( ( column.getQuotedName( dialect ) ) ); createIndex.append( column.getText( dialect ) );
if ( columnOrderMap.containsKey( column ) ) { if ( columnOrderMap.containsKey( column ) ) {
buf.append( " " ).append( columnOrderMap.get( column ) ); createIndex.append( " " ).append( columnOrderMap.get( column ) );
} }
} }
buf.append( ")" );
return new String[] { buf.toString() };
} }
@Override @Override
@ -87,7 +93,7 @@ public class StandardIndexExporter implements Exporter<Index> {
final String tableName = context.format( index.getTable().getQualifiedTableName() ); final String tableName = context.format( index.getTable().getQualifiedTableName() );
final String indexNameForCreation = dialect.qualifyIndexName() final String indexNameForCreation = dialect.qualifyIndexName()
? StringHelper.qualify( tableName, index.getName() ) ? qualify( tableName, index.getName() )
: index.getName(); : index.getName();
return new String[] { "drop index " + indexNameForCreation }; return new String[] { "drop index " + indexNameForCreation };

View File

@ -33,12 +33,12 @@ public class UniqueConstraintValidationTest extends BaseUnitTestCase {
buildSessionFactory(EmptyColumnNameEntity.class); buildSessionFactory(EmptyColumnNameEntity.class);
} }
@Test @Test(expected = AnnotationException.class)
public void testUniqueConstraintWithEmptyColumnNameList() { public void testUniqueConstraintWithEmptyColumnNameList() {
buildSessionFactory(EmptyColumnNameListEntity.class); buildSessionFactory(EmptyColumnNameListEntity.class);
} }
@Test(expected = AnnotationException.class) @Test
public void testUniqueConstraintWithNotExistsColumnName() { public void testUniqueConstraintWithNotExistsColumnName() {
buildSessionFactory(NotExistsColumnEntity.class); buildSessionFactory(NotExistsColumnEntity.class);
} }