HHH-17103 massively simplify @Index + @UniqueKey handling
There was a whole completely unnecessary second-pass-based lifecycle making everything way more complicated than it needed to be.
This commit is contained in:
parent
5bfe11fd27
commit
367a647412
|
@ -19,7 +19,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.DuplicateMappingException;
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -57,9 +56,6 @@ import org.hibernate.boot.model.internal.SetBasicValueTypeSecondPass;
|
|||
import org.hibernate.boot.model.internal.UniqueConstraintHolder;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.naming.ImplicitForeignKeyNameSource;
|
||||
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.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.model.relational.ExportableProducer;
|
||||
|
@ -92,19 +88,15 @@ import org.hibernate.mapping.Component;
|
|||
import org.hibernate.mapping.DenormalizedTable;
|
||||
import org.hibernate.mapping.FetchProfile;
|
||||
import org.hibernate.mapping.ForeignKey;
|
||||
import org.hibernate.mapping.Formula;
|
||||
import org.hibernate.mapping.IdentifierCollection;
|
||||
import org.hibernate.mapping.Index;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
import org.hibernate.mapping.MappedSuperclass;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.RootClass;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.metamodel.CollectionClassification;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.query.named.NamedObjectRepository;
|
||||
|
@ -122,7 +114,6 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.MapsId;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
|
||||
/**
|
||||
|
@ -181,8 +172,6 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
private Map<String, String> mappedByResolver;
|
||||
private Map<String, String> propertyRefResolver;
|
||||
private Set<DelayedPropertyReferenceHandler> delayedPropertyReferenceHandlers;
|
||||
private Map<Table, List<UniqueConstraintHolder>> uniqueConstraintHoldersByTable;
|
||||
private Map<Table, List<IndexHolder>> indexHoldersByTable;
|
||||
private List<Function<MetadataBuildingContext, Boolean>> valueResolvers;
|
||||
|
||||
public InFlightMetadataCollectorImpl(
|
||||
|
@ -1440,59 +1429,16 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
|
||||
@Override @Deprecated(forRemoval = true)
|
||||
public void addUniqueConstraints(Table table, List<String[]> uniqueConstraints) {
|
||||
List<UniqueConstraintHolder> constraintHolders = new ArrayList<>( uniqueConstraints.size() );
|
||||
|
||||
int keyNameBase = determineCurrentNumberOfUniqueConstraintHolders( table );
|
||||
for ( String[] columns : uniqueConstraints ) {
|
||||
final String keyName = "key" + keyNameBase++;
|
||||
constraintHolders.add(
|
||||
new UniqueConstraintHolder( keyName, columns )
|
||||
);
|
||||
}
|
||||
addUniqueConstraintHolders( table, constraintHolders );
|
||||
throw new UnsupportedOperationException("addUniqueConstraints() will be removed");
|
||||
}
|
||||
|
||||
private int determineCurrentNumberOfUniqueConstraintHolders(Table table) {
|
||||
List currentHolders = uniqueConstraintHoldersByTable == null ? null : uniqueConstraintHoldersByTable.get( table );
|
||||
return currentHolders == null ? 0 : currentHolders.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @Deprecated(forRemoval = true)
|
||||
public void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> holders) {
|
||||
List<UniqueConstraintHolder> holderList = null;
|
||||
|
||||
if ( uniqueConstraintHoldersByTable == null ) {
|
||||
uniqueConstraintHoldersByTable = new HashMap<>();
|
||||
}
|
||||
else {
|
||||
holderList = uniqueConstraintHoldersByTable.get( table );
|
||||
}
|
||||
|
||||
if ( holderList == null ) {
|
||||
holderList = new ArrayList<>();
|
||||
uniqueConstraintHoldersByTable.put( table, holderList );
|
||||
}
|
||||
|
||||
holderList.addAll( holders );
|
||||
throw new UnsupportedOperationException("addUniqueConstraintHolders() will be removed");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override @Deprecated(forRemoval = true)
|
||||
public void addIndexHolders(Table table, List<IndexHolder> holders) {
|
||||
List<IndexHolder> holderList = null;
|
||||
|
||||
if ( indexHoldersByTable == null ) {
|
||||
indexHoldersByTable = new HashMap<>();
|
||||
}
|
||||
else {
|
||||
holderList = indexHoldersByTable.get( table );
|
||||
}
|
||||
|
||||
if ( holderList == null ) {
|
||||
holderList = new ArrayList<>();
|
||||
indexHoldersByTable.put( table, holderList );
|
||||
}
|
||||
|
||||
holderList.addAll( holders );
|
||||
throw new UnsupportedOperationException("addIndexHolders() will be removed");
|
||||
}
|
||||
|
||||
private final Map<String,EntityTableXrefImpl> entityTableXrefMap = new HashMap<>();
|
||||
|
@ -1848,9 +1794,6 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
|
||||
secondPassCompileForeignKeys( buildingContext );
|
||||
|
||||
processUniqueConstraintHolders( buildingContext );
|
||||
processIndexHolders( buildingContext );
|
||||
|
||||
processNaturalIdUniqueKeyBinders();
|
||||
|
||||
processCachingOverrides();
|
||||
|
@ -2044,18 +1987,6 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
}
|
||||
}
|
||||
|
||||
private List<Identifier> toIdentifiers(String[] names) {
|
||||
if ( names == null ) {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
final List<Identifier> columnNames = arrayList( names.length );
|
||||
for ( String name : names ) {
|
||||
columnNames.add( getDatabase().toIdentifier( name ) );
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
private List<Identifier> extractColumnNames(List<Column> columns) {
|
||||
if ( columns == null || columns.isEmpty() ) {
|
||||
return emptyList();
|
||||
|
@ -2082,141 +2013,6 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
delayedPropertyReferenceHandlers.clear();
|
||||
}
|
||||
|
||||
private void processUniqueConstraintHolders(MetadataBuildingContext buildingContext) {
|
||||
if ( uniqueConstraintHoldersByTable == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( Map.Entry<Table, List<UniqueConstraintHolder>> tableListEntry : uniqueConstraintHoldersByTable.entrySet() ) {
|
||||
final Table table = tableListEntry.getKey();
|
||||
final List<UniqueConstraintHolder> uniqueConstraints = tableListEntry.getValue();
|
||||
for ( UniqueConstraintHolder holder : uniqueConstraints ) {
|
||||
buildUniqueKeyFromColumnNames( table, holder.getName(), holder.isNameExplicit(), holder.getColumns(), buildingContext );
|
||||
}
|
||||
}
|
||||
|
||||
uniqueConstraintHoldersByTable.clear();
|
||||
}
|
||||
|
||||
private void buildUniqueKeyFromColumnNames(
|
||||
Table table,
|
||||
String keyName,
|
||||
boolean nameExplicit,
|
||||
String[] columnNames,
|
||||
MetadataBuildingContext context) {
|
||||
buildUniqueKeyFromColumnNames( table, keyName, nameExplicit, columnNames, null, true, context );
|
||||
}
|
||||
|
||||
private void buildUniqueKeyFromColumnNames(
|
||||
final Table table,
|
||||
String keyName,
|
||||
boolean nameExplicit,
|
||||
final String[] columnNames,
|
||||
String[] orderings,
|
||||
boolean unique,
|
||||
final MetadataBuildingContext context) {
|
||||
final int size = columnNames.length;
|
||||
if ( size == 0 ) {
|
||||
throw new AnnotationException( ( unique ? "Unique constraint" : "Index" )
|
||||
+ ( isEmpty( keyName ) ? "" : " '" + keyName + "'" )
|
||||
+ " on table '" + table.getName() + "' has no columns" );
|
||||
}
|
||||
final Selectable[] columns = new Selectable[size];
|
||||
for ( int index = 0; index < size; 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 {
|
||||
column = table.getColumn( buildingContext.getMetadataCollector(), logicalColumnName );
|
||||
}
|
||||
catch (MappingException me) {
|
||||
column = null;
|
||||
}
|
||||
if ( column != null ) {
|
||||
return column;
|
||||
}
|
||||
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'"
|
||||
// );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createIndexOrUniqueKey(
|
||||
Table table,
|
||||
String originalKeyName,
|
||||
boolean nameExplicit,
|
||||
String[] columnNames,
|
||||
String[] orderings,
|
||||
boolean unique,
|
||||
MetadataBuildingContext context,
|
||||
Selectable[] columns) {
|
||||
final ImplicitNamingStrategy naming = getMetadataBuildingOptions().getImplicitNamingStrategy();
|
||||
final IndexOrUniqueKeyNameSource source =
|
||||
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 {
|
||||
final String keyName = naming.determineIndexName( source ).render( dialect );
|
||||
final Index index = table.getOrCreateIndex( keyName );
|
||||
index.setUnique( unique );
|
||||
for ( int i = 0; i < columns.length; i++ ) {
|
||||
index.addColumn( columns[i], orderings != null ? orderings[i] : null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processIndexHolders(MetadataBuildingContext context) {
|
||||
if ( indexHoldersByTable != null ) {
|
||||
for ( Map.Entry<Table, List<IndexHolder>> entry : indexHoldersByTable.entrySet() ) {
|
||||
final Table table = entry.getKey();
|
||||
final List<IndexHolder> indexHolders = entry.getValue();
|
||||
for ( IndexHolder holder : indexHolders) {
|
||||
buildUniqueKeyFromColumnNames(
|
||||
table,
|
||||
holder.getName(),
|
||||
!holder.getName().isEmpty(),
|
||||
holder.getColumns(),
|
||||
holder.getOrdering(),
|
||||
holder.isUnique(),
|
||||
context
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String,NaturalIdUniqueKeyBinder> naturalIdUniqueKeyBinderMap;
|
||||
|
||||
@Override
|
||||
|
@ -2394,46 +2190,6 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
}
|
||||
}
|
||||
|
||||
private class IndexOrUniqueKeyNameSource implements ImplicitIndexNameSource, ImplicitUniqueKeyNameSource {
|
||||
private final MetadataBuildingContext buildingContext;
|
||||
private final Table table;
|
||||
private final String[] columnNames;
|
||||
private final String originalKeyName;
|
||||
|
||||
public IndexOrUniqueKeyNameSource(MetadataBuildingContext buildingContext, Table table, String[] columnNames, String originalKeyName) {
|
||||
this.buildingContext = buildingContext;
|
||||
this.table = table;
|
||||
this.columnNames = columnNames;
|
||||
this.originalKeyName = originalKeyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataBuildingContext getBuildingContext() {
|
||||
return buildingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTableName() {
|
||||
return table.getNameIdentifier();
|
||||
}
|
||||
|
||||
private List<Identifier> columnNameIdentifiers;
|
||||
|
||||
@Override
|
||||
public List<Identifier> getColumnNames() {
|
||||
// be lazy about building these
|
||||
if ( columnNameIdentifiers == null ) {
|
||||
columnNameIdentifiers = toIdentifiers(columnNames);
|
||||
}
|
||||
return columnNameIdentifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getUserProvidedIdentifier() {
|
||||
return originalKeyName != null ? Identifier.toIdentifier(originalKeyName) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private class ForeignKeyNameSource implements ImplicitForeignKeyNameSource {
|
||||
final List<Identifier> columnNames;
|
||||
private final ForeignKey foreignKey;
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
|
|||
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.model.source.spi.AttributePath;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -433,17 +434,14 @@ public class AnnotatedColumn {
|
|||
|
||||
protected void addColumnBinding(SimpleValue value) {
|
||||
final String logicalColumnName;
|
||||
final MetadataBuildingContext context = getBuildingContext();
|
||||
final InFlightMetadataCollector collector = context.getMetadataCollector();
|
||||
if ( isNotEmpty( this.logicalColumnName ) ) {
|
||||
logicalColumnName = this.logicalColumnName;
|
||||
}
|
||||
else {
|
||||
final ObjectNameNormalizer normalizer = getBuildingContext().getObjectNameNormalizer();
|
||||
final Database database = getBuildingContext().getMetadataCollector().getDatabase();
|
||||
final ImplicitNamingStrategy implicitNamingStrategy = getBuildingContext().getBuildingOptions()
|
||||
.getImplicitNamingStrategy();
|
||||
|
||||
final Identifier implicitName = normalizer.normalizeIdentifierQuoting(
|
||||
implicitNamingStrategy.determineBasicColumnName(
|
||||
final Identifier implicitName = context.getObjectNameNormalizer().normalizeIdentifierQuoting(
|
||||
context.getBuildingOptions().getImplicitNamingStrategy().determineBasicColumnName(
|
||||
new ImplicitBasicColumnNameSource() {
|
||||
@Override
|
||||
public AttributePath getAttributePath() {
|
||||
|
@ -457,14 +455,14 @@ public class AnnotatedColumn {
|
|||
|
||||
@Override
|
||||
public MetadataBuildingContext getBuildingContext() {
|
||||
return AnnotatedColumn.this.getBuildingContext();
|
||||
return context;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
logicalColumnName = implicitName.render( database.getDialect() );
|
||||
logicalColumnName = implicitName.render( collector.getDatabase().getDialect() );
|
||||
}
|
||||
getBuildingContext().getMetadataCollector().addColumnNameBinding( value.getTable(), logicalColumnName, getMappingColumn() );
|
||||
collector.addColumnNameBinding( value.getTable(), logicalColumnName, getMappingColumn() );
|
||||
}
|
||||
|
||||
public void forceNotNull() {
|
||||
|
|
|
@ -12,8 +12,6 @@ import java.util.Map;
|
|||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.internal.util.MutableInteger;
|
||||
|
@ -69,26 +67,7 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
|||
@Override
|
||||
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
||||
final PersistentClass referencedPersistentClass = persistentClasses.get( referencedEntityName );
|
||||
if ( referencedPersistentClass == null ) {
|
||||
// TODO: much better error message if this is something that can really happen!
|
||||
throw new AnnotationException( "Unknown entity name '" + referencedEntityName + "'");
|
||||
}
|
||||
final KeyValue identifier = referencedPersistentClass.getIdentifier();
|
||||
if ( !(identifier instanceof Component) ) {
|
||||
// The entity with the @MapsId annotation has a composite
|
||||
// id type, but the referenced entity has a basic-typed id.
|
||||
// Therefore, the @MapsId annotation should have specified
|
||||
// a property of the composite id that has the foreign key
|
||||
throw new AnnotationException(
|
||||
"Missing 'value' in '@MapsId' annotation of association '" + propertyName
|
||||
+ "' of entity '" + component.getOwner().getEntityName()
|
||||
+ "' with composite identifier type"
|
||||
+ " ('@MapsId' must specify a property of the '@EmbeddedId' class which has the foreign key of '"
|
||||
+ referencedEntityName + "')"
|
||||
);
|
||||
}
|
||||
|
||||
final Component referencedComponent = (Component) identifier;
|
||||
final Component referencedComponent = getReferencedComponent( referencedPersistentClass );
|
||||
|
||||
//prepare column name structure
|
||||
boolean isExplicitReference = true;
|
||||
|
@ -113,7 +92,6 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
|||
final Property property;
|
||||
if ( referencedProperty.isComposite() ) {
|
||||
property = createComponentProperty(
|
||||
referencedPersistentClass,
|
||||
isExplicitReference,
|
||||
columnByReferencedName,
|
||||
index,
|
||||
|
@ -133,8 +111,29 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
|||
}
|
||||
}
|
||||
|
||||
private Component getReferencedComponent(PersistentClass referencedPersistentClass) {
|
||||
if ( referencedPersistentClass == null ) {
|
||||
// TODO: much better error message if this is something that can really happen!
|
||||
throw new AnnotationException( "Unknown entity name '" + referencedEntityName + "'");
|
||||
}
|
||||
final KeyValue identifier = referencedPersistentClass.getIdentifier();
|
||||
if ( !(identifier instanceof Component) ) {
|
||||
// The entity with the @MapsId annotation has a composite
|
||||
// id type, but the referenced entity has a basic-typed id.
|
||||
// Therefore, the @MapsId annotation should have specified
|
||||
// a property of the composite id that has the foreign key
|
||||
throw new AnnotationException(
|
||||
"Missing 'value' in '@MapsId' annotation of association '" + propertyName
|
||||
+ "' of entity '" + component.getOwner().getEntityName()
|
||||
+ "' with composite identifier type"
|
||||
+ " ('@MapsId' must specify a property of the '@EmbeddedId' class which has the foreign key of '"
|
||||
+ referencedEntityName + "')"
|
||||
);
|
||||
}
|
||||
return (Component) identifier;
|
||||
}
|
||||
|
||||
private Property createComponentProperty(
|
||||
PersistentClass referencedPersistentClass,
|
||||
boolean isExplicitReference,
|
||||
Map<String, AnnotatedJoinColumn> columnByReferencedName,
|
||||
MutableInteger index,
|
||||
|
@ -156,12 +155,21 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
|||
|
||||
for ( Property referencedComponentProperty : referencedValue.getProperties() ) {
|
||||
if ( referencedComponentProperty.isComposite() ) {
|
||||
Property componentProperty = createComponentProperty( referencedValue.getOwner(), isExplicitReference, columnByReferencedName, index, referencedComponentProperty );
|
||||
value.addProperty( componentProperty );
|
||||
value.addProperty( createComponentProperty(
|
||||
isExplicitReference,
|
||||
columnByReferencedName,
|
||||
index,
|
||||
referencedComponentProperty
|
||||
) );
|
||||
}
|
||||
else {
|
||||
Property componentProperty = createSimpleProperty( referencedValue.getOwner(), isExplicitReference, columnByReferencedName, index, referencedComponentProperty );
|
||||
value.addProperty( componentProperty );
|
||||
value.addProperty( createSimpleProperty(
|
||||
referencedValue.getOwner(),
|
||||
isExplicitReference,
|
||||
columnByReferencedName,
|
||||
index,
|
||||
referencedComponentProperty
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,10 +236,11 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
|||
: joinColumn.getName();
|
||||
|
||||
final Database database = buildingContext.getMetadataCollector().getDatabase();
|
||||
final PhysicalNamingStrategy physicalNamingStrategy = buildingContext.getBuildingOptions().getPhysicalNamingStrategy();
|
||||
final Identifier explicitName = database.toIdentifier( columnName );
|
||||
final Identifier physicalName = physicalNamingStrategy.toPhysicalColumnName( explicitName, database.getJdbcEnvironment() );
|
||||
value.addColumn( new Column( physicalName.render( database.getDialect() ) ) );
|
||||
final String physicalName =
|
||||
buildingContext.getBuildingOptions().getPhysicalNamingStrategy()
|
||||
.toPhysicalColumnName( database.toIdentifier( columnName ), database.getJdbcEnvironment() )
|
||||
.render( database.getDialect() );
|
||||
value.addColumn( new Column( physicalName ) );
|
||||
if ( joinColumn != null ) {
|
||||
applyComponentColumnSizeValueToJoinColumn( column, joinColumn );
|
||||
joinColumn.linkWithValue( value );
|
||||
|
@ -243,7 +252,7 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
|||
}
|
||||
|
||||
private void applyComponentColumnSizeValueToJoinColumn(Column column, AnnotatedJoinColumn joinColumn) {
|
||||
Column mappingColumn = joinColumn.getMappingColumn();
|
||||
final Column mappingColumn = joinColumn.getMappingColumn();
|
||||
mappingColumn.setLength( column.getLength() );
|
||||
mappingColumn.setPrecision( column.getPrecision() );
|
||||
mappingColumn.setScale( column.getScale() );
|
||||
|
|
|
@ -2051,8 +2051,7 @@ public class EntityBinder {
|
|||
secondaryTable.uniqueConstraints()
|
||||
);
|
||||
final Table table = join.getTable();
|
||||
context.getMetadataCollector()
|
||||
.addIndexHolders( table, TableBinder.buildIndexHolders( secondaryTable.indexes() ) );
|
||||
new IndexBinder( context ).bindIndexes( table, secondaryTable.indexes() );
|
||||
return join;
|
||||
}
|
||||
|
||||
|
@ -2239,9 +2238,7 @@ public class EntityBinder {
|
|||
|
||||
public void processComplementaryTableDefinitions(jakarta.persistence.Table table) {
|
||||
if ( table != null ) {
|
||||
final Table classTable = persistentClass.getTable();
|
||||
context.getMetadataCollector()
|
||||
.addIndexHolders( classTable, TableBinder.buildIndexHolders( table.indexes() ) );
|
||||
new IndexBinder( context ).bindIndexes( persistentClass.getTable(), table.indexes() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.boot.model.internal;
|
||||
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
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.PhysicalNamingStrategy;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Formula;
|
||||
import org.hibernate.mapping.Index;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
|
||||
/**
|
||||
* Responsible for interpreting {@link jakarta.persistence.Index} and
|
||||
* {@link UniqueConstraint} annotations.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
class IndexBinder {
|
||||
|
||||
private final MetadataBuildingContext context;
|
||||
|
||||
IndexBinder(MetadataBuildingContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private Database getDatabase() {
|
||||
return context.getMetadataCollector().getDatabase();
|
||||
}
|
||||
|
||||
private ImplicitNamingStrategy getImplicitNamingStrategy() {
|
||||
return context.getBuildingOptions().getImplicitNamingStrategy();
|
||||
}
|
||||
|
||||
private PhysicalNamingStrategy getPhysicalNamingStrategy() {
|
||||
return context.getBuildingOptions().getPhysicalNamingStrategy();
|
||||
}
|
||||
|
||||
private Dialect getDialect() {
|
||||
return getDatabase().getJdbcEnvironment().getDialect();
|
||||
}
|
||||
|
||||
private Selectable selectable(Table table, String columnNameOrFormula) {
|
||||
if ( columnNameOrFormula.startsWith("(") ) {
|
||||
return new Formula( columnNameOrFormula );
|
||||
}
|
||||
else {
|
||||
return createColumn( columnNameOrFormula );
|
||||
// Column column;
|
||||
// try {
|
||||
// column = table.getColumn( context.getMetadataCollector(), columnNameOrFormula );
|
||||
// }
|
||||
// catch (MappingException me) {
|
||||
// column = null;
|
||||
// }
|
||||
// return column == null
|
||||
// // Assume it's actually a formula with missing parens
|
||||
// ? new Formula( "(" + columnNameOrFormula + ")" )
|
||||
// : column;
|
||||
}
|
||||
}
|
||||
|
||||
private Column column(Table table, String columnName) {
|
||||
return createColumn( columnName );
|
||||
// Column column;
|
||||
// try {
|
||||
// column = table.getColumn( context.getMetadataCollector(), columnName );
|
||||
// }
|
||||
// catch (MappingException me) {
|
||||
// column = null;
|
||||
// }
|
||||
// if ( column != null ) {
|
||||
// return column;
|
||||
// }
|
||||
// else {
|
||||
// throw new AnnotationException(
|
||||
// "Table '" + table.getName() + "' has no column named '" + columnName
|
||||
// + "' matching the column specified in '@UniqueConstraint'"
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
private Column createColumn(String logicalName) {
|
||||
final Database database = getDatabase();
|
||||
final String physicalName =
|
||||
getPhysicalNamingStrategy()
|
||||
.toPhysicalColumnName( database.toIdentifier( logicalName ), database.getJdbcEnvironment() )
|
||||
.render( getDialect() );
|
||||
return new Column( physicalName );
|
||||
}
|
||||
|
||||
private Selectable[] selectables(Table table, String name, final String[] columnNames) {
|
||||
final int size = columnNames.length;
|
||||
if ( size == 0 ) {
|
||||
throw new AnnotationException( "Index"
|
||||
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
|
||||
+ " on table '" + table.getName() + "' has no columns" );
|
||||
}
|
||||
final Selectable[] columns = new Selectable[size];
|
||||
for ( int index = 0; index < size; index++ ) {
|
||||
final String columnName = columnNames[index];
|
||||
if ( isEmpty( columnName ) ) {
|
||||
throw new AnnotationException( "Index"
|
||||
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
|
||||
+ " on table '" + table.getName() + "' has an empty column name" );
|
||||
}
|
||||
columns[index] = selectable( table, columnName );
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
private Column[] columns(Table table, String name, final String[] columnNames) {
|
||||
final int size = columnNames.length;
|
||||
if ( size == 0 ) {
|
||||
throw new AnnotationException( "Unique constraint"
|
||||
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
|
||||
+ " on table '" + table.getName() + "' has no columns" );
|
||||
}
|
||||
final Column[] columns = new Column[size];
|
||||
for ( int index = 0; index < size; index++ ) {
|
||||
final String columnName = columnNames[index];
|
||||
if ( isEmpty( columnName ) ) {
|
||||
throw new AnnotationException( "Unique constraint"
|
||||
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
|
||||
+ " on table '" + table.getName() + "' has an empty column name" );
|
||||
}
|
||||
columns[index] = column( table, columnName );
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
private void createIndexOrUniqueKey(
|
||||
Table table,
|
||||
String originalKeyName,
|
||||
boolean nameExplicit,
|
||||
String[] columnNames,
|
||||
String[] orderings,
|
||||
boolean unique,
|
||||
Selectable[] columns) {
|
||||
final IndexOrUniqueKeyNameSource source =
|
||||
new IndexOrUniqueKeyNameSource( context, table, columnNames, originalKeyName );
|
||||
boolean hasFormula = false;
|
||||
for ( Selectable selectable : columns ) {
|
||||
if ( selectable.isFormula() ) {
|
||||
hasFormula = true;
|
||||
}
|
||||
}
|
||||
if ( unique && !hasFormula ) {
|
||||
final String keyName = getImplicitNamingStrategy().determineUniqueKeyName( source ).render( getDialect() );
|
||||
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 {
|
||||
final String keyName = getImplicitNamingStrategy().determineIndexName( source ).render( getDialect() );
|
||||
final Index index = table.getOrCreateIndex( keyName );
|
||||
index.setUnique( unique );
|
||||
for ( int i = 0; i < columns.length; i++ ) {
|
||||
index.addColumn( columns[i], orderings != null ? orderings[i] : null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bindIndexes(Table table, jakarta.persistence.Index[] indexes) {
|
||||
for ( jakarta.persistence.Index index : indexes ) {
|
||||
final StringTokenizer tokenizer = new StringTokenizer( index.columnList(), "," );
|
||||
final List<String> parsed = new ArrayList<>();
|
||||
while ( tokenizer.hasMoreElements() ) {
|
||||
final String trimmed = tokenizer.nextToken().trim();
|
||||
if ( !trimmed.isEmpty() ) {
|
||||
parsed.add( trimmed ) ;
|
||||
}
|
||||
}
|
||||
final String[] columnExpressions = new String[parsed.size()];
|
||||
final String[] ordering = new String[parsed.size()];
|
||||
initializeColumns( columnExpressions, ordering, parsed );
|
||||
final String name = index.name();
|
||||
final boolean unique = index.unique();
|
||||
createIndexOrUniqueKey( table, name, !name.isEmpty(), columnExpressions, ordering, unique,
|
||||
selectables( table, name, columnExpressions ) );
|
||||
}
|
||||
}
|
||||
|
||||
void bindUniqueConstraints(Table table, UniqueConstraint[] constraints) {
|
||||
for ( UniqueConstraint constraint : constraints ) {
|
||||
final String name = constraint.name();
|
||||
final String[] columnNames = constraint.columnNames();
|
||||
createIndexOrUniqueKey( table, name, !name.isEmpty(), columnNames, null, true,
|
||||
columns( table, name, columnNames ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeColumns(String[] columns, String[] ordering, List<String> list) {
|
||||
for ( int i = 0, size = list.size(); i < size; i++ ) {
|
||||
final String description = list.get( i );
|
||||
final String tmp = description.toLowerCase(Locale.ROOT);
|
||||
if ( tmp.endsWith( " desc" ) ) {
|
||||
columns[i] = description.substring( 0, description.length() - 5 );
|
||||
ordering[i] = "desc";
|
||||
}
|
||||
else if ( tmp.endsWith( " asc" ) ) {
|
||||
columns[i] = description.substring( 0, description.length() - 4 );
|
||||
ordering[i] = "asc";
|
||||
}
|
||||
else {
|
||||
columns[i] = description;
|
||||
ordering[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IndexOrUniqueKeyNameSource implements ImplicitIndexNameSource, ImplicitUniqueKeyNameSource {
|
||||
private final MetadataBuildingContext buildingContext;
|
||||
private final Table table;
|
||||
private final String[] columnNames;
|
||||
private final String originalKeyName;
|
||||
|
||||
public IndexOrUniqueKeyNameSource(MetadataBuildingContext buildingContext, Table table, String[] columnNames, String originalKeyName) {
|
||||
this.buildingContext = buildingContext;
|
||||
this.table = table;
|
||||
this.columnNames = columnNames;
|
||||
this.originalKeyName = originalKeyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetadataBuildingContext getBuildingContext() {
|
||||
return buildingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTableName() {
|
||||
return table.getNameIdentifier();
|
||||
}
|
||||
|
||||
private List<Identifier> columnNameIdentifiers;
|
||||
|
||||
@Override
|
||||
public List<Identifier> getColumnNames() {
|
||||
// be lazy about building these
|
||||
if ( columnNameIdentifiers == null ) {
|
||||
columnNameIdentifiers = toIdentifiers( columnNames );
|
||||
}
|
||||
return columnNameIdentifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getUserProvidedIdentifier() {
|
||||
return originalKeyName != null ? Identifier.toIdentifier( originalKeyName ) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Identifier> toIdentifiers(String[] names) {
|
||||
if ( names == null ) {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
final List<Identifier> columnNames = arrayList( names.length );
|
||||
for ( String name : names ) {
|
||||
columnNames.add( getDatabase().toIdentifier( name ) );
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,10 @@ import jakarta.persistence.Index;
|
|||
|
||||
/**
|
||||
* @author Strong Liu
|
||||
*
|
||||
* @deprecated no longer used, will be removed in next release
|
||||
*/
|
||||
@Deprecated(since = "6.3", forRemoval = true)
|
||||
public class IndexHolder {
|
||||
private final String name;
|
||||
private final String[] columns;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.boot.model.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
|
@ -43,13 +42,10 @@ import org.jboss.logging.Logger;
|
|||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.isQuoted;
|
||||
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.unquote;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
|
||||
|
||||
/**
|
||||
* Stateful binder responsible for producing instances of {@link Table}.
|
||||
|
@ -488,12 +484,12 @@ public class TableBinder {
|
|||
logicalName, isAbstract, buildingContext, subselect,
|
||||
denormalizedSuperTableXref, metadataCollector );
|
||||
|
||||
if ( isNotEmpty( uniqueConstraints ) ) {
|
||||
metadataCollector.addUniqueConstraintHolders( table, buildUniqueConstraintHolders( uniqueConstraints ) );
|
||||
if ( uniqueConstraints != null ) {
|
||||
new IndexBinder( buildingContext ).bindUniqueConstraints( table, uniqueConstraints );
|
||||
}
|
||||
|
||||
if ( isNotEmpty( indexes ) ) {
|
||||
metadataCollector.addIndexHolders( table, buildIndexHolders( indexes ) );
|
||||
if ( indexes != null ) {
|
||||
new IndexBinder( buildingContext ).bindIndexes( table, indexes );
|
||||
}
|
||||
|
||||
metadataCollector.addTableNameBinding( logicalName, table );
|
||||
|
@ -815,35 +811,6 @@ public class TableBinder {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of {@link IndexHolder} instances given a
|
||||
* list of {@link Index} annotations.
|
||||
*/
|
||||
static List<IndexHolder> buildIndexHolders(Index[] indexes) {
|
||||
List<IndexHolder> holders = new ArrayList<>( indexes.length );
|
||||
for ( Index index : indexes ) {
|
||||
holders.add( new IndexHolder( index ) );
|
||||
}
|
||||
return holders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of {@link UniqueConstraintHolder} instances
|
||||
* given a list of {@link UniqueConstraint} annotations.
|
||||
*/
|
||||
static List<UniqueConstraintHolder> buildUniqueConstraintHolders(UniqueConstraint[] uniqueConstraints) {
|
||||
if ( uniqueConstraints == null || uniqueConstraints.length == 0 ) {
|
||||
return emptyList();
|
||||
}
|
||||
else {
|
||||
final List<UniqueConstraintHolder> result = arrayList( uniqueConstraints.length );
|
||||
for ( UniqueConstraint uniqueConstraint : uniqueConstraints ) {
|
||||
result.add( new UniqueConstraintHolder( uniqueConstraint ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultName(
|
||||
String ownerClassName,
|
||||
String ownerEntity,
|
||||
|
|
|
@ -10,14 +10,13 @@ package org.hibernate.boot.model.internal;
|
|||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
/**
|
||||
* {@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
|
||||
* 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).
|
||||
* {@link jakarta.persistence.UniqueConstraint} annotations are handled via second pass.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @deprecated no longer used, will be removed in next release
|
||||
*/
|
||||
// Isn't this ultimately the same as IndexOrUniqueKeySecondPass?
|
||||
@Deprecated(since = "6.3", forRemoval = true)
|
||||
public class UniqueConstraintHolder {
|
||||
private final String name;
|
||||
private final String[] columns;
|
||||
|
|
|
@ -354,11 +354,19 @@ public interface InFlightMetadataCollector extends MetadataImplementor {
|
|||
String getFromMappedBy(String ownerEntityName, String propertyName);
|
||||
|
||||
/**
|
||||
* @deprecated no longer used
|
||||
* @deprecated no longer implemented, will be removed in next release
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
void addUniqueConstraints(Table table, List<String[]> uniqueConstraints);
|
||||
/**
|
||||
* @deprecated no longer implemented, will be removed in next release
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> uniqueConstraints);
|
||||
/**
|
||||
* @deprecated no longer implemented, will be removed in next release
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
void addIndexHolders(Table table, List<IndexHolder> indexHolders);
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.Map;
|
|||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||
|
||||
/**
|
||||
* A mapping model object representing a {@linkplain jakarta.persistence.UniqueConstraint unique key}
|
||||
* constraint on a relational database table.
|
||||
|
@ -35,7 +37,7 @@ public class UniqueKey extends Constraint {
|
|||
|
||||
public void addColumn(Column column, String order) {
|
||||
addColumn( column );
|
||||
if ( StringHelper.isNotEmpty( order ) ) {
|
||||
if ( isNotEmpty( order ) ) {
|
||||
columnOrderMap.put( column, order );
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +65,8 @@ public class UniqueKey extends Constraint {
|
|||
|
||||
public boolean hasNullableColumn() {
|
||||
for ( Column column : getColumns() ) {
|
||||
if ( column.isNullable() ) {
|
||||
final Column tableColumn = getTable().getColumn( column );
|
||||
if ( tableColumn != null && tableColumn.isNullable() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue