HHH-8217 constraint name fixes for metamodel

This commit is contained in:
Brett Meyer 2013-05-13 12:44:03 -04:00
parent 96ce0d2742
commit ca4516a4e9
13 changed files with 219 additions and 84 deletions

View File

@ -23,6 +23,8 @@
*/ */
package org.hibernate.metamodel.internal; package org.hibernate.metamodel.internal;
import static org.hibernate.engine.spi.SyntheticAttributeHelper.SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -36,8 +38,6 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.MultiTenancyStrategy; import org.hibernate.MultiTenancyStrategy;
@ -46,6 +46,7 @@ import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.cfg.ObjectNameNormalizer; import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.cfg.ObjectNameNormalizer.NamingStrategyHelper;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
@ -176,9 +177,7 @@ import org.hibernate.type.ComponentType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.jboss.logging.Logger;
import static org.hibernate.cfg.ObjectNameNormalizer.NamingStrategyHelper;
import static org.hibernate.engine.spi.SyntheticAttributeHelper.SYNTHETIC_COMPOSITE_ID_ATTRIBUTE_NAME;
/** /**
* The common binder shared between annotations and {@code hbm.xml} processing. * The common binder shared between annotations and {@code hbm.xml} processing.
@ -2106,13 +2105,15 @@ public class Binder {
) )
); );
if ( elementSource.isUnique() ) { if ( elementSource.isUnique() ) {
UniqueKey uk = collectionTable.getOrCreateUniqueKey( StringHelper.randomFixedLengthHex( UniqueKey uk = new UniqueKey();
UniqueKey.GENERATED_NAME_PREFIX ) );
for ( RelationalValueBinding relationalValueBinding : elementBinding.getRelationalValueBindings() ) { for ( RelationalValueBinding relationalValueBinding : elementBinding.getRelationalValueBindings() ) {
if ( ! relationalValueBinding.isDerived() ) { if ( ! relationalValueBinding.isDerived() ) {
uk.addColumn( (Column) relationalValueBinding.getValue() ); uk.addColumn( (Column) relationalValueBinding.getValue() );
} }
} }
uk.setTable( collectionTable );
uk.generateName();
collectionTable.addUniqueKey( uk );
} }
if ( !elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().isInverse() && if ( !elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().isInverse() &&
!elementBinding.hasDerivedValue() ) { !elementBinding.hasDerivedValue() ) {
@ -2764,14 +2765,19 @@ public class Binder {
final EntitySource entitySource) { final EntitySource entitySource) {
for ( final ConstraintSource constraintSource : entitySource.getConstraints() ) { for ( final ConstraintSource constraintSource : entitySource.getConstraints() ) {
if ( UniqueConstraintSource.class.isInstance( constraintSource ) ) { if ( UniqueConstraintSource.class.isInstance( constraintSource ) ) {
UniqueKey uk = new UniqueKey();
final TableSpecification table = entityBinding.locateTable( constraintSource.getTableName() ); final TableSpecification table = entityBinding.locateTable( constraintSource.getTableName() );
final List<String> columnNames = constraintSource.columnNames();
final String constraintName = StringHelper.isEmpty( constraintSource.name() ) final String constraintName = StringHelper.isEmpty( constraintSource.name() )
? StringHelper.randomFixedLengthHex( UniqueKey.GENERATED_NAME_PREFIX ) ? UniqueKey.generateName( table, columnNames.toArray( new String[columnNames.size()] ) )
: constraintSource.name(); : constraintSource.name();
final UniqueKey uniqueKey = table.getOrCreateUniqueKey( constraintName ); for ( final String columnName : columnNames ) {
for ( final String columnName : constraintSource.columnNames() ) { uk.addColumn( table.locateOrCreateColumn( quotedIdentifier( columnName ) ) );
uniqueKey.addColumn( table.locateOrCreateColumn( quotedIdentifier( columnName ) ) );
} }
uk.setTable( table );
uk.setName( constraintName );
table.addUniqueKey( uk );
} }
} }
} }
@ -2948,8 +2954,11 @@ public class Binder {
column.setComment( columnSource.getComment() ); column.setComment( columnSource.getComment() );
if (columnSource.isUnique()) { if (columnSource.isUnique()) {
table.getOrCreateUniqueKey( StringHelper.randomFixedLengthHex( UniqueKey uk = new UniqueKey();
UniqueKey.GENERATED_NAME_PREFIX ) ).addColumn( column ); uk.addColumn( column );
uk.setTable( table );
uk.generateName();
table.addUniqueKey( uk );
} }
return column; return column;
} }

View File

@ -25,17 +25,17 @@ package org.hibernate.metamodel.internal;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.spi.relational.Column; import org.hibernate.metamodel.spi.relational.Column;
import org.hibernate.metamodel.spi.relational.ForeignKey; import org.hibernate.metamodel.spi.relational.ForeignKey;
import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.metamodel.spi.relational.TableSpecification;
import org.jboss.logging.Logger;
/** /**
* @author Gail Badner * @author Gail Badner
* @author Brett Meyer
*/ */
public class ForeignKeyHelper { public class ForeignKeyHelper {
private static final CoreMessageLogger log = Logger.getMessageLogger( private static final CoreMessageLogger log = Logger.getMessageLogger(
@ -55,16 +55,14 @@ public class ForeignKeyHelper {
final List<Column> sourceColumns, final List<Column> sourceColumns,
final TableSpecification targetTable, final TableSpecification targetTable,
final List<Column> targetColumns) { final List<Column> targetColumns) {
ForeignKey foreignKey = null; if ( StringHelper.isEmpty( foreignKeyName ) ) {
if ( foreignKeyName != null ) { foreignKeyName = ForeignKey.generateName( sourceTable, targetTable, sourceColumns, targetColumns );
foreignKey = locateAndBindForeignKeyByName( foreignKeyName, sourceTable, sourceColumns, targetTable, targetColumns );
}
else {
foreignKeyName = StringHelper.randomFixedLengthHex( ForeignKey.GENERATED_NAME_PREFIX );
} }
ForeignKey foreignKey = locateAndBindForeignKeyByName( foreignKeyName, sourceTable, sourceColumns, targetTable, targetColumns );
if ( foreignKey == null ) { if ( foreignKey == null ) {
foreignKey = locateForeignKeyByColumnMapping( sourceTable, sourceColumns, targetTable, targetColumns ); foreignKey = locateForeignKeyByColumnMapping( sourceTable, sourceColumns, targetTable, targetColumns );
if ( foreignKey != null && foreignKeyName != null ) { if ( foreignKey != null ) {
if ( foreignKey.getName() == null ) { if ( foreignKey.getName() == null ) {
// the foreign key name has not be initialized; set it to foreignKeyName // the foreign key name has not be initialized; set it to foreignKeyName
foreignKey.setName( foreignKeyName ); foreignKey.setName( foreignKeyName );
@ -163,7 +161,7 @@ public class ForeignKeyHelper {
// The located foreign key already has columns bound; // The located foreign key already has columns bound;
// Make sure they are the same columns. // Make sure they are the same columns.
if ( !foreignKey.getSourceColumns().equals( sourceColumns ) || if ( !foreignKey.getSourceColumns().equals( sourceColumns ) ||
foreignKey.getTargetColumns().equals( targetColumns ) ) { !foreignKey.getTargetColumns().equals( targetColumns ) ) {
throw binder.bindingContext().makeMappingException( throw binder.bindingContext().makeMappingException(
String.format( String.format(
"Attempt to bind exisitng foreign key \"%s\" with different columns.", "Attempt to bind exisitng foreign key \"%s\" with different columns.",

View File

@ -23,7 +23,7 @@ package org.hibernate.metamodel.internal;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.spi.relational.AbstractConstraint;
import org.hibernate.metamodel.spi.relational.Column; import org.hibernate.metamodel.spi.relational.Column;
import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.metamodel.spi.relational.TableSpecification;
import org.hibernate.metamodel.spi.relational.UniqueKey; import org.hibernate.metamodel.spi.relational.UniqueKey;
@ -51,8 +51,11 @@ public class UniqueKeyHelper {
uniqueKey = naturalIdUniqueKeys.get( table ); uniqueKey = naturalIdUniqueKeys.get( table );
} }
else { else {
uniqueKey = table.getOrCreateUniqueKey( String keyName = "UK_" + AbstractConstraint.hashedName( table.getLogicalName().getText() + "_NaturalID" );
StringHelper.randomFixedLengthHex( UniqueKey.GENERATED_NAME_PREFIX ) ); uniqueKey = new UniqueKey();
uniqueKey.setTable( table );
uniqueKey.setName( keyName );
table.addUniqueKey( uniqueKey );
naturalIdUniqueKeys.put( table, uniqueKey ); naturalIdUniqueKeys.put( table, uniqueKey );
} }
uniqueKey.addColumn( column ); uniqueKey.addColumn( column );

View File

@ -52,7 +52,7 @@ class UniqueConstraintSourceImpl implements UniqueConstraintSource {
} }
@Override @Override
public Iterable<String> columnNames() { public List<String> columnNames() {
return columnNames; return columnNames;
} }

View File

@ -23,12 +23,16 @@
*/ */
package org.hibernate.metamodel.spi.relational; package org.hibernate.metamodel.spi.relational;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
/** /**
@ -40,7 +44,7 @@ import org.hibernate.dialect.Dialect;
* @author Gail Badner * @author Gail Badner
*/ */
public abstract class AbstractConstraint implements Constraint { public abstract class AbstractConstraint implements Constraint {
private final TableSpecification table; private TableSpecification table;
private String name; private String name;
private final Map<Identifier, Column> columnMap = new LinkedHashMap<Identifier, Column>(); private final Map<Identifier, Column> columnMap = new LinkedHashMap<Identifier, Column>();
@ -54,6 +58,10 @@ public abstract class AbstractConstraint implements Constraint {
return table; return table;
} }
public void setTable( TableSpecification table ) {
this.table = table;
}
/** /**
* Returns the constraint name, or null if the name has not been set. * Returns the constraint name, or null if the name has not been set.
* *
@ -88,6 +96,33 @@ public abstract class AbstractConstraint implements Constraint {
this.name = name; this.name = name;
} }
/**
* Hash a constraint name using MD5. Convert the MD5 digest to base 35
* (full alphanumeric), guaranteeing
* that the length of the name will always be smaller than the 30
* character identifier restriction enforced by a few dialects.
*
* @param s
* The name to be hashed.
* @return String The hased name.
*/
public static String hashedName(String s) {
try {
MessageDigest md = MessageDigest.getInstance( "MD5" );
md.reset();
md.update( s.getBytes() );
byte[] digest = md.digest();
BigInteger bigInt = new BigInteger( 1, digest );
// By converting to base 35 (full alphanumeric), we guarantee
// that the length of the name will always be smaller than the 30
// character identifier restriction enforced by a few dialects.
return bigInt.toString( 35 );
}
catch ( NoSuchAlgorithmException e ) {
throw new HibernateException( "Unable to generate a hashed Constraint name!", e );
}
}
protected int generateConstraintColumnListId() { protected int generateConstraintColumnListId() {
return table.generateColumnListId( getColumns() ); return table.generateColumnListId( getColumns() );
} }

View File

@ -24,15 +24,16 @@
package org.hibernate.metamodel.spi.relational; package org.hibernate.metamodel.spi.relational;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.jboss.logging.Logger;
/** /**
* Models the notion of a foreign key. * Models the notion of a foreign key.
@ -42,11 +43,10 @@ import org.hibernate.dialect.Dialect;
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
* @author Brett Meyer
*/ */
public class ForeignKey extends AbstractConstraint { public class ForeignKey extends AbstractConstraint {
public static final String GENERATED_NAME_PREFIX = "FK";
private static final Logger LOG = Logger.getLogger( ForeignKey.class ); private static final Logger LOG = Logger.getLogger( ForeignKey.class );
private static final String ON_DELETE = " on delete "; private static final String ON_DELETE = " on delete ";
@ -137,11 +137,6 @@ public class ForeignKey extends AbstractConstraint {
} }
} }
@Override
public String getExportIdentifier() {
return getSourceTable().getLoggableValueQualifier() + ".FK-" + getName();
}
public ReferentialAction getDeleteRule() { public ReferentialAction getDeleteRule() {
return deleteRule; return deleteRule;
} }
@ -258,4 +253,54 @@ public class ForeignKey extends AbstractConstraint {
} }
return columnMappingList; return columnMappingList;
} }
/**
* If a constraint is not explicitly named, this is called to generate
* a unique hash using the source/target table and column names.
* Static so the name can be generated prior to creating the Constraint.
* They're cached, keyed by name, in multiple locations.
*
* @param sourceTable
* @param targetTable
* @param sourceColumns
* @param targetColumns
* @return String The generated name
*/
public static String generateName(TableSpecification sourceTable, TableSpecification targetTable,
List<Column> sourceColumns, List<Column> targetColumns) {
// Use a concatenation that guarantees uniqueness, even if identical names
// exist between all table and column identifiers.
StringBuilder sb = new StringBuilder( "table`" + sourceTable.getLogicalName().getText() + "`" );
sb.append( "table`" + targetTable.getLogicalName().getText() + "`" );
appendColumns( sourceColumns, sb );
appendColumns( targetColumns, sb );
return "FK_" + hashedName( sb.toString() );
}
private static void appendColumns(List<Column> columns, StringBuilder sb) {
// Ensure a consistent ordering of columns, regardless of the order
// they were bound.
// Clone the list, as sometimes a set of order-dependent Column
// bindings are given.
Column[] alphabeticalColumns = columns.toArray( new Column[columns.size()] );
Arrays.sort( alphabeticalColumns, ColumnComparator.INSTANCE );
for ( Column column : alphabeticalColumns ) {
sb.append( "column`" + column.getColumnName().getText() + "`" );
}
}
private static class ColumnComparator implements Comparator<Column> {
public static ColumnComparator INSTANCE = new ColumnComparator();
public int compare(Column col1, Column col2) {
return col1.getColumnName().toString().compareTo( col2.getColumnName().toString() );
}
}
@Override
public String getExportIdentifier() {
return getSourceTable().getLoggableValueQualifier() + ".FK-" + getName();
}
} }

View File

@ -79,7 +79,7 @@ public class InLineView extends AbstractTableSpecification {
} }
@Override @Override
public UniqueKey getOrCreateUniqueKey(String name) { public void addUniqueKey(UniqueKey uk) {
throw new UnsupportedOperationException( "Cannot create unique-key on inline view" ); throw new UnsupportedOperationException( "Cannot create unique-key on inline view" );
} }

View File

@ -33,22 +33,10 @@ import org.hibernate.dialect.Dialect;
*/ */
public class Index extends AbstractConstraint { public class Index extends AbstractConstraint {
public static final String GENERATED_NAME_PREFIX = "IDX";
protected Index(Table table, String name) { protected Index(Table table, String name) {
super( table, name ); super( table, name );
} }
@Override
public String getExportIdentifier() {
StringBuilder sb = new StringBuilder( getTable().getLoggableValueQualifier());
sb.append( ".IDX" );
for ( Column column : getColumns() ) {
sb.append( '_' ).append( column.getColumnName().getText() );
}
return sb.toString();
}
@Override @Override
public String sqlConstraintStringInAlterTable(Dialect dialect) { public String sqlConstraintStringInAlterTable(Dialect dialect) {
StringBuilder buf = new StringBuilder( " index (" ); StringBuilder buf = new StringBuilder( " index (" );
@ -64,4 +52,14 @@ public class Index extends AbstractConstraint{
} }
return buf.append( ')' ).toString(); return buf.append( ')' ).toString();
} }
@Override
public String getExportIdentifier() {
StringBuilder sb = new StringBuilder( getTable().getLoggableValueQualifier() );
sb.append( ".IDX" );
for ( Column column : getColumns() ) {
sb.append( '_' ).append( column.getColumnName().getText() );
}
return sb.toString();
}
} }

View File

@ -36,17 +36,10 @@ import org.hibernate.dialect.Dialect;
*/ */
public class PrimaryKey extends AbstractConstraint { public class PrimaryKey extends AbstractConstraint {
public static final String GENERATED_NAME_PREFIX = "PK";
protected PrimaryKey(TableSpecification table) { protected PrimaryKey(TableSpecification table) {
super( table, null ); super( table, null );
} }
@Override
public String getExportIdentifier() {
return getTable().getLoggableValueQualifier() + ".PK";
}
public String sqlConstraintStringInCreateTable(Dialect dialect) { public String sqlConstraintStringInCreateTable(Dialect dialect) {
StringBuilder buf = new StringBuilder("primary key ("); StringBuilder buf = new StringBuilder("primary key (");
boolean first = true; boolean first = true;
@ -79,4 +72,9 @@ public class PrimaryKey extends AbstractConstraint {
return buf.append(')').toString(); return buf.append(')').toString();
} }
@Override
public String getExportIdentifier() {
return getTable().getLoggableValueQualifier() + ".PK";
}
} }

View File

@ -164,19 +164,9 @@ public class Table extends AbstractTableSpecification implements Exportable {
return Collections.unmodifiableSet( uniqueKeys ); return Collections.unmodifiableSet( uniqueKeys );
} }
// TODO: The "get" part of this should probably go away -- all callers
// should now be creating only.
@Override @Override
public UniqueKey getOrCreateUniqueKey(String name) { public void addUniqueKey(UniqueKey uk) {
UniqueKey result = null; uniqueKeys.add( uk );
if ( name != null ) {
result = locateConstraint( uniqueKeys, name );
}
if ( result == null ) {
result = new UniqueKey( this, name );
uniqueKeys.add( result );
}
return result;
} }
@Override @Override

View File

@ -140,7 +140,7 @@ public interface TableSpecification extends ValueContainer, Loggable {
public Iterable<UniqueKey> getUniqueKeys(); public Iterable<UniqueKey> getUniqueKeys();
public UniqueKey getOrCreateUniqueKey(String name); public void addUniqueKey(UniqueKey uk);
public boolean hasUniqueKey(Column column); public boolean hasUniqueKey(Column column);

View File

@ -23,6 +23,9 @@
*/ */
package org.hibernate.metamodel.spi.relational; package org.hibernate.metamodel.spi.relational;
import java.util.Arrays;
import java.util.List;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
/** /**
@ -30,25 +33,18 @@ import org.hibernate.dialect.Dialect;
* *
* @author Gavin King * @author Gavin King
* @author Steve Ebersole * @author Steve Ebersole
* @author Brett Meyer
*/ */
public class UniqueKey extends AbstractConstraint { public class UniqueKey extends AbstractConstraint {
public static final String GENERATED_NAME_PREFIX = "UK"; public UniqueKey() {
super( null, null );
}
protected UniqueKey(Table table, String name) { protected UniqueKey(Table table, String name) {
super( table, name ); super( table, name );
} }
@Override
public String getExportIdentifier() {
StringBuilder sb = new StringBuilder( getTable().getLoggableValueQualifier() );
sb.append( ".UK" );
for ( Column column : getColumns() ) {
sb.append( '_' ).append( column.getColumnName().getText() );
}
return sb.toString();
}
@Override @Override
public String[] sqlCreateStrings(Dialect dialect) { public String[] sqlCreateStrings(Dialect dialect) {
return new String[] { dialect.getUniqueDelegate().getAlterTableToAddUniqueKeyCommand( this ) }; return new String[] { dialect.getUniqueDelegate().getAlterTableToAddUniqueKeyCommand( this ) };
@ -64,4 +60,65 @@ public class UniqueKey extends AbstractConstraint {
// not used // not used
return ""; return "";
} }
/**
* If a constraint is not explicitly named, this is called to generate
* a unique hash using the table and column names.
* Static so the name can be generated prior to creating the Constraint.
* They're cached, keyed by name, in multiple locations.
*
* @param table
* @param columnNames
* @return String The generated name
*/
public static String generateName(TableSpecification table, String... columnNames) {
// Use a concatenation that guarantees uniqueness, even if identical names
// exist between all table and column identifiers.
StringBuilder sb = new StringBuilder( "table`" + table.getLogicalName().getText() + "`" );
// Ensure a consistent ordering of columns, regardless of the order
// they were bound.
// Clone the list, as sometimes a set of order-dependent Column
// bindings are given.
String[] alphabeticalColumns = columnNames.clone();
Arrays.sort( alphabeticalColumns );
for ( String columnName : alphabeticalColumns ) {
sb.append( "column`" + columnName + "`" );
}
return "UK_" + hashedName( sb.toString() );
}
/**
* Helper method for {@link #generateName(String, TableSpecification, String...)}.
*
* @param table
* @param columns
* @return String The generated name
*/
public static String generateName(TableSpecification table, List<Column> columns) {
String[] columnNames = new String[columns.size()];
for ( int i = 0; i < columns.size(); i++ ) {
columnNames[i] = columns.get( i ).getColumnName().getText();
}
return generateName( table, columnNames );
}
/**
* Helper method for {@link #generateName(String, TableSpecification, String...)}.
* Generates and sets a name for this existing constraint.
*/
public void generateName() {
setName( generateName( getTable(), getColumns() ) );
}
@Override
public String getExportIdentifier() {
StringBuilder sb = new StringBuilder( getTable().getLoggableValueQualifier() );
sb.append( ".UK" );
for ( Column column : getColumns() ) {
sb.append( '_' ).append( column.getColumnName().getText() );
}
return sb.toString();
}
} }

View File

@ -23,6 +23,8 @@
*/ */
package org.hibernate.metamodel.spi.source; package org.hibernate.metamodel.spi.source;
import java.util.List;
/** /**
* Contract describing source of table constraints * Contract describing source of table constraints
* *
@ -44,5 +46,5 @@ public interface ConstraintSource {
/** /**
* @return returns the names of the column which are part of this constraint * @return returns the names of the column which are part of this constraint
*/ */
Iterable<String> columnNames(); public List<String> columnNames();
} }