clean up foreign key binding code
This commit is contained in:
parent
264d3c711c
commit
239fc9a835
|
@ -6,10 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.cfg;
|
package org.hibernate.cfg;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||||
|
|
||||||
|
@ -779,73 +778,60 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
|
||||||
AnnotatedJoinColumn[] columns,
|
AnnotatedJoinColumn[] columns,
|
||||||
PersistentClass referencedEntity,
|
PersistentClass referencedEntity,
|
||||||
MetadataBuildingContext context) {
|
MetadataBuildingContext context) {
|
||||||
//convenient container to find whether a column is an id one or not
|
if ( columns.length == 0 ) {
|
||||||
Set<Column> idColumns = new HashSet<>();
|
return NO_REFERENCE; //shortcut
|
||||||
for ( Selectable selectable : referencedEntity.getKey().getSelectables() ) {
|
|
||||||
idColumns.add( (Column) selectable );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isFkReferencedColumnName = false;
|
final Object columnOwner = findColumnOwner( referencedEntity, columns[0].getReferencedColumn(), context );
|
||||||
boolean noReferencedColumn = true;
|
|
||||||
//build the list of potential tables
|
|
||||||
if ( columns.length == 0 ) return NO_REFERENCE; //shortcut
|
|
||||||
Object columnOwner = findColumnOwner( referencedEntity, columns[0].getReferencedColumn(), context );
|
|
||||||
if ( columnOwner == null ) {
|
if ( columnOwner == null ) {
|
||||||
try {
|
try {
|
||||||
throw new MappingException(
|
throw new MappingException(
|
||||||
"No column with logical name '"
|
"A '@JoinColumn' references a column named '" + columns[0].getReferencedColumn()
|
||||||
+ columns[0].getReferencedColumn()
|
+ "' but the target entity '" + referencedEntity.getEntityName()
|
||||||
+ "' in table '" + referencedEntity.getTable().getName()
|
+ "' has no property which maps to this column"
|
||||||
+ "' for entity '" + referencedEntity.getEntityName()
|
|
||||||
+ "', nor in its related superclass tables and secondary tables"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (MappingException e) {
|
catch (MappingException e) {
|
||||||
throw new RecoverableException( e.getMessage(), e );
|
throw new RecoverableException( e.getMessage(), e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Table matchingTable = columnOwner instanceof PersistentClass ?
|
final Table table = columnOwner instanceof PersistentClass
|
||||||
( (PersistentClass) columnOwner ).getTable() :
|
? ( (PersistentClass) columnOwner ).getTable()
|
||||||
( (Join) columnOwner ).getTable();
|
: ( (Join) columnOwner ).getTable();
|
||||||
//check each referenced column
|
|
||||||
for (AnnotatedJoinColumn ejb3Column : columns) {
|
final List<Selectable> keyColumns = referencedEntity.getKey().getSelectables();
|
||||||
String logicalReferencedColumnName = ejb3Column.getReferencedColumn();
|
boolean explicitColumnReference = false;
|
||||||
|
for ( AnnotatedJoinColumn column : columns ) {
|
||||||
|
final String logicalReferencedColumnName = column.getReferencedColumn();
|
||||||
if ( StringHelper.isNotEmpty( logicalReferencedColumnName ) ) {
|
if ( StringHelper.isNotEmpty( logicalReferencedColumnName ) ) {
|
||||||
String referencedColumnName;
|
explicitColumnReference = true;
|
||||||
try {
|
if ( !keyColumns.contains( column( context, table, logicalReferencedColumnName ) ) ) {
|
||||||
referencedColumnName = context.getMetadataCollector().getPhysicalColumnName(
|
// we have a column which does not belong to the PK
|
||||||
matchingTable,
|
return NON_PK_REFERENCE;
|
||||||
logicalReferencedColumnName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (MappingException me) {
|
|
||||||
//rewrite the exception
|
|
||||||
throw new MappingException(
|
|
||||||
"No column with logical name '" + logicalReferencedColumnName
|
|
||||||
+ "' in table '" + matchingTable.getName() + "'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
noReferencedColumn = false;
|
|
||||||
Column refCol = new Column( referencedColumnName );
|
|
||||||
boolean contains = idColumns.contains( refCol );
|
|
||||||
if ( !contains ) {
|
|
||||||
isFkReferencedColumnName = true;
|
|
||||||
break; //we know the state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( isFkReferencedColumnName ) {
|
if ( explicitColumnReference ) {
|
||||||
return NON_PK_REFERENCE;
|
// if we got to here, all the columns belong to the PK
|
||||||
}
|
return keyColumns.size() == columns.length
|
||||||
else if ( noReferencedColumn ) {
|
// we have all the PK columns
|
||||||
return NO_REFERENCE;
|
? PK_REFERENCE
|
||||||
}
|
// we have a subset of the PK columns
|
||||||
else if ( idColumns.size() != columns.length ) {
|
: NON_PK_REFERENCE;
|
||||||
//reference use PK but is a subset or a superset
|
|
||||||
return NON_PK_REFERENCE;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return PK_REFERENCE;
|
// there were no nonempty referencedColumnNames
|
||||||
|
return NO_REFERENCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Column column(MetadataBuildingContext context, Table table, String logicalReferencedColumnName) {
|
||||||
|
try {
|
||||||
|
return new Column( context.getMetadataCollector().getPhysicalColumnName( table, logicalReferencedColumnName ) );
|
||||||
|
}
|
||||||
|
catch (MappingException me) {
|
||||||
|
throw new MappingException( "No column with logical name '" + logicalReferencedColumnName
|
||||||
|
+ "' in table '" + table.getName() + "'" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
|
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
|
||||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP;
|
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP;
|
||||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.interpret;
|
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.interpret;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Emmanuel Bernard
|
* @author Emmanuel Bernard
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,6 +41,6 @@ public class JoinedSubclassFkSecondPass extends FkSecondPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
||||||
TableBinder.bindFk( entity.getSuperclass(), entity, columns, value, false, buildingContext );
|
TableBinder.bindForeignKey( entity.getSuperclass(), entity, columns, value, false, buildingContext );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class ToOneFkSecondPass extends FkSecondPass {
|
||||||
}
|
}
|
||||||
manyToOne.setPropertyName( path );
|
manyToOne.setPropertyName( path );
|
||||||
createSyntheticPropertyReference( columns, ref, null, manyToOne, false, buildingContext );
|
createSyntheticPropertyReference( columns, ref, null, manyToOne, false, buildingContext );
|
||||||
TableBinder.bindFk( ref, null, columns, manyToOne, unique, buildingContext );
|
TableBinder.bindForeignKey( ref, null, columns, manyToOne, unique, buildingContext );
|
||||||
/*
|
/*
|
||||||
* HbmMetadataSourceProcessorImpl does this only when property-ref != null, but IMO, it makes sense event if it is null
|
* HbmMetadataSourceProcessorImpl does this only when property-ref != null, but IMO, it makes sense event if it is null
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2653,7 +2653,7 @@ public abstract class CollectionBinder {
|
||||||
if ( property.isAnnotationPresent( ElementCollection.class ) && joinColumns.length > 0 ) {
|
if ( property.isAnnotationPresent( ElementCollection.class ) && joinColumns.length > 0 ) {
|
||||||
joinColumns[0].setJPA2ElementCollection( true );
|
joinColumns[0].setJPA2ElementCollection( true );
|
||||||
}
|
}
|
||||||
TableBinder.bindFk( collValue.getOwner(), collectionEntity, joinColumns, key, false, buildingContext );
|
TableBinder.bindForeignKey( collValue.getOwner(), collectionEntity, joinColumns, key, false, buildingContext );
|
||||||
key.sortProperties();
|
key.sortProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2703,7 +2703,7 @@ public abstract class CollectionBinder {
|
||||||
if ( notFoundAction == NotFoundAction.IGNORE ) {
|
if ( notFoundAction == NotFoundAction.IGNORE ) {
|
||||||
value.disableForeignKey();
|
value.disableForeignKey();
|
||||||
}
|
}
|
||||||
TableBinder.bindFk( referencedEntity, null, columns, value, unique, buildingContext );
|
TableBinder.bindForeignKey( referencedEntity, null, columns, value, unique, buildingContext );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1813,7 +1813,7 @@ public class EntityBinder {
|
||||||
join.setKey( key );
|
join.setKey( key );
|
||||||
setFKNameIfDefined( join );
|
setFKNameIfDefined( join );
|
||||||
key.setCascadeDeleteEnabled( false );
|
key.setCascadeDeleteEnabled( false );
|
||||||
TableBinder.bindFk( persistentClass, null, annotatedJoinColumns, key, false, buildingContext );
|
TableBinder.bindForeignKey( persistentClass, null, annotatedJoinColumns, key, false, buildingContext );
|
||||||
key.sortProperties();
|
key.sortProperties();
|
||||||
join.createPrimaryKey();
|
join.createPrimaryKey();
|
||||||
join.createForeignKey();
|
join.createForeignKey();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.cfg.AnnotatedJoinColumn;
|
||||||
import org.hibernate.cfg.IndexOrUniqueKeySecondPass;
|
import org.hibernate.cfg.IndexOrUniqueKeySecondPass;
|
||||||
import org.hibernate.cfg.JPAIndexHolder;
|
import org.hibernate.cfg.JPAIndexHolder;
|
||||||
import org.hibernate.cfg.ObjectNameSource;
|
import org.hibernate.cfg.ObjectNameSource;
|
||||||
|
import org.hibernate.cfg.PropertyHolder;
|
||||||
import org.hibernate.cfg.UniqueConstraintHolder;
|
import org.hibernate.cfg.UniqueConstraintHolder;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
@ -46,6 +47,9 @@ import org.hibernate.mapping.Value;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.cfg.AnnotatedJoinColumn.NON_PK_REFERENCE;
|
||||||
|
import static org.hibernate.cfg.AnnotatedJoinColumn.NO_REFERENCE;
|
||||||
|
import static org.hibernate.cfg.AnnotatedJoinColumn.checkReferencedColumnsType;
|
||||||
import static org.hibernate.cfg.BinderHelper.isEmptyOrNullAnnotationValue;
|
import static org.hibernate.cfg.BinderHelper.isEmptyOrNullAnnotationValue;
|
||||||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||||
import static org.hibernate.internal.util.StringHelper.isQuoted;
|
import static org.hibernate.internal.util.StringHelper.isQuoted;
|
||||||
|
@ -525,7 +529,7 @@ public class TableBinder {
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void bindFk(
|
public static void bindForeignKey(
|
||||||
PersistentClass referencedEntity,
|
PersistentClass referencedEntity,
|
||||||
PersistentClass destinationEntity,
|
PersistentClass destinationEntity,
|
||||||
AnnotatedJoinColumn[] columns,
|
AnnotatedJoinColumn[] columns,
|
||||||
|
@ -538,163 +542,23 @@ public class TableBinder {
|
||||||
associatedClass = destinationEntity;
|
associatedClass = destinationEntity;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
associatedClass = columns[0].getPropertyHolder() == null
|
PropertyHolder holder = columns[0].getPropertyHolder();
|
||||||
? null
|
associatedClass = holder == null ? null : holder.getPersistentClass();
|
||||||
: columns[0].getPropertyHolder().getPersistentClass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String mappedByProperty = columns[0].getMappedBy();
|
final String mappedByProperty = columns[0].getMappedBy();
|
||||||
if ( isNotEmpty( mappedByProperty ) ) {
|
if ( isNotEmpty( mappedByProperty ) ) {
|
||||||
// Get the columns of the mapped-by property
|
// use the columns of the property referenced by mappedBy
|
||||||
// copy them and link the copy to the actual value
|
// copy them and link the copy to the actual value
|
||||||
LOG.debugf( "Retrieving property %s.%s", associatedClass.getEntityName(), mappedByProperty );
|
bindUnownedAssociation( columns, value, associatedClass, mappedByProperty );
|
||||||
|
|
||||||
final Value propertyVal = associatedClass.getRecursiveProperty( columns[0].getMappedBy() ).getValue();
|
|
||||||
final List<Column> mappedByColumns;
|
|
||||||
if ( propertyVal instanceof Collection ) {
|
|
||||||
Value element = ((Collection) propertyVal).getElement();
|
|
||||||
if ( element == null ) {
|
|
||||||
throw new AnnotationException(
|
|
||||||
"Both sides of the bidirectional association '"
|
|
||||||
+ associatedClass.getEntityName() + "." + mappedByProperty
|
|
||||||
+ "' specify 'mappedBy'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
mappedByColumns = element.getColumns();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mappedByColumns = propertyVal.getColumns();
|
|
||||||
}
|
|
||||||
for ( Column column: mappedByColumns ) {
|
|
||||||
columns[0].overrideFromReferencedColumnIfNecessary( column );
|
|
||||||
columns[0].linkValueUsingAColumnCopy( column, value );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ( columns[0].isImplicit() ) {
|
else if ( columns[0].isImplicit() ) {
|
||||||
// if columns are implicit, then create the columns based on the
|
// if columns are implicit, then create the columns based
|
||||||
// referenced entity id columns
|
// on the referenced entity id columns
|
||||||
List<Column> idColumns = referencedEntity instanceof JoinedSubclass
|
bindImplicitColumns( referencedEntity, columns, value );
|
||||||
? referencedEntity.getKey().getColumns()
|
|
||||||
: referencedEntity.getIdentifier().getColumns();
|
|
||||||
for ( Column column: idColumns ) {
|
|
||||||
columns[0].linkValueUsingDefaultColumnNaming( column, referencedEntity, value );
|
|
||||||
columns[0].overrideFromReferencedColumnIfNecessary( column );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int fkEnum = AnnotatedJoinColumn.checkReferencedColumnsType( columns, referencedEntity, buildingContext );
|
bindExplicitColumns( referencedEntity, columns, value, buildingContext, associatedClass );
|
||||||
if ( AnnotatedJoinColumn.NON_PK_REFERENCE == fkEnum ) {
|
|
||||||
String referencedPropertyName;
|
|
||||||
if ( value instanceof ToOne ) {
|
|
||||||
referencedPropertyName = ( (ToOne) value ).getReferencedPropertyName();
|
|
||||||
}
|
|
||||||
else if ( value instanceof DependantValue ) {
|
|
||||||
String propertyName = columns[0].getPropertyName();
|
|
||||||
if ( propertyName != null ) {
|
|
||||||
Collection collection = (Collection)
|
|
||||||
referencedEntity.getRecursiveProperty( propertyName ).getValue();
|
|
||||||
referencedPropertyName = collection.getReferencedPropertyName();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new AnnotationException( "The '@JoinColumn' for a secondary table must reference the primary key" );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new AssertionFailure(
|
|
||||||
"Do a property ref on an unexpected Value type: "
|
|
||||||
+ value.getClass().getName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ( referencedPropertyName == null ) {
|
|
||||||
throw new AssertionFailure(
|
|
||||||
"No property ref found while expected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Property synthProp = referencedEntity.getReferencedProperty( referencedPropertyName );
|
|
||||||
if ( synthProp == null ) {
|
|
||||||
throw new AssertionFailure(
|
|
||||||
"Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
linkJoinColumnWithValueOverridingNameIfImplicit( referencedEntity, synthProp.getValue(), columns, value );
|
|
||||||
if ( value instanceof SortableValue ) {
|
|
||||||
( (SortableValue) value ).sortProperties();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( AnnotatedJoinColumn.NO_REFERENCE == fkEnum ) {
|
|
||||||
//implicit case, we hope PK and FK columns are in the same order
|
|
||||||
if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
|
|
||||||
throw new AnnotationException(
|
|
||||||
"A foreign key that references '" + referencedEntity.getEntityName()
|
|
||||||
+ "' from entity '" + associatedClass.getEntityName()
|
|
||||||
+ "' has " + columns.length + " columns but the primary key has "
|
|
||||||
+ referencedEntity.getIdentifier().getColumnSpan() + " columns"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
linkJoinColumnWithValueOverridingNameIfImplicit(
|
|
||||||
referencedEntity,
|
|
||||||
referencedEntity.getIdentifier(),
|
|
||||||
columns,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
if ( value instanceof SortableValue ) {
|
|
||||||
( (SortableValue) value ).sortProperties();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Ensure the component is sorted so that we can simply set sorted to true on the to-one
|
|
||||||
KeyValue key = referencedEntity.getKey();
|
|
||||||
if ( key instanceof Component ) {
|
|
||||||
( (Component) key).sortProperties();
|
|
||||||
}
|
|
||||||
//explicit referencedColumnName
|
|
||||||
List<Column> idColumns = key.getColumns();
|
|
||||||
//works cause the pk has to be on the primary table
|
|
||||||
if ( idColumns.isEmpty() ) {
|
|
||||||
LOG.debug( "No column in the identifier" );
|
|
||||||
}
|
|
||||||
final Dialect dialect = buildingContext.getMetadataCollector().getDatabase()
|
|
||||||
.getJdbcEnvironment().getDialect();
|
|
||||||
for ( Column col: idColumns ) {
|
|
||||||
boolean match = false;
|
|
||||||
//for each PK column, find the associated FK column.
|
|
||||||
final String colName = col.getQuotedName(dialect);
|
|
||||||
for ( AnnotatedJoinColumn joinCol : columns ) {
|
|
||||||
String referencedColumn = joinCol.getReferencedColumn();
|
|
||||||
referencedColumn = buildingContext.getMetadataCollector().getPhysicalColumnName(
|
|
||||||
referencedEntity.getTable(),
|
|
||||||
referencedColumn
|
|
||||||
);
|
|
||||||
//In JPA 2 referencedColumnName is case-insensitive
|
|
||||||
if ( referencedColumn.equalsIgnoreCase( colName ) ) {
|
|
||||||
//proper join column
|
|
||||||
if ( joinCol.isNameDeferred() ) {
|
|
||||||
joinCol.linkValueUsingDefaultColumnNaming(
|
|
||||||
col, referencedEntity, value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
joinCol.linkWithValue( value );
|
|
||||||
}
|
|
||||||
joinCol.overrideFromReferencedColumnIfNecessary( col );
|
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !match ) {
|
|
||||||
throw new AnnotationException(
|
|
||||||
"Column name " + col.getName() + " of "
|
|
||||||
+ referencedEntity.getEntityName() + " not found in JoinColumns.referencedColumnName"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( value instanceof ToOne ) {
|
|
||||||
( (ToOne) value ).setSorted( true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
value.createForeignKey();
|
value.createForeignKey();
|
||||||
if ( unique ) {
|
if ( unique ) {
|
||||||
|
@ -702,15 +566,186 @@ public class TableBinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void bindExplicitColumns(
|
||||||
|
PersistentClass referencedEntity,
|
||||||
|
AnnotatedJoinColumn[] columns,
|
||||||
|
SimpleValue value,
|
||||||
|
MetadataBuildingContext buildingContext,
|
||||||
|
PersistentClass associatedClass) {
|
||||||
|
switch ( checkReferencedColumnsType( columns, referencedEntity, buildingContext ) ) {
|
||||||
|
case NON_PK_REFERENCE: {
|
||||||
|
bindNonPkReference( referencedEntity, columns, value );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NO_REFERENCE: {
|
||||||
|
bindImplicitPkReference( referencedEntity, columns, value, associatedClass );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
bindPkReference( referencedEntity, columns, value, associatedClass, buildingContext );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindImplicitPkReference(
|
||||||
|
PersistentClass referencedEntity,
|
||||||
|
AnnotatedJoinColumn[] columns,
|
||||||
|
SimpleValue value,
|
||||||
|
PersistentClass associatedClass) {
|
||||||
|
//implicit case, we hope PK and FK columns are in the same order
|
||||||
|
if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
|
||||||
|
throw new AnnotationException(
|
||||||
|
"An association that targets entity '" + referencedEntity.getEntityName()
|
||||||
|
+ "' from entity '" + associatedClass.getEntityName()
|
||||||
|
+ "' has " + columns.length + " '@JoinColumn's but the primary key has "
|
||||||
|
+ referencedEntity.getIdentifier().getColumnSpan() + " columns"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
linkJoinColumnWithValueOverridingNameIfImplicit(
|
||||||
|
referencedEntity,
|
||||||
|
referencedEntity.getIdentifier(),
|
||||||
|
columns,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
if ( value instanceof SortableValue ) {
|
||||||
|
( (SortableValue) value).sortProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindPkReference(
|
||||||
|
PersistentClass referencedEntity,
|
||||||
|
AnnotatedJoinColumn[] columns,
|
||||||
|
SimpleValue value,
|
||||||
|
PersistentClass associatedClass,
|
||||||
|
MetadataBuildingContext buildingContext) {
|
||||||
|
// ensure the composite key is sorted so that we can simply
|
||||||
|
// set sorted to true on the ToOne (below)
|
||||||
|
final KeyValue key = referencedEntity.getKey();
|
||||||
|
if ( key instanceof Component ) {
|
||||||
|
( (Component) key).sortProperties();
|
||||||
|
}
|
||||||
|
// works because the pk has to be on the primary table
|
||||||
|
final Dialect dialect = buildingContext.getMetadataCollector().getDatabase()
|
||||||
|
.getJdbcEnvironment().getDialect();
|
||||||
|
for ( Column col: key.getColumns() ) {
|
||||||
|
boolean match = false;
|
||||||
|
// for each PK column, find the associated FK column.
|
||||||
|
final String colName = col.getQuotedName( dialect );
|
||||||
|
for ( AnnotatedJoinColumn joinCol : columns ) {
|
||||||
|
final String referencedColumn = buildingContext.getMetadataCollector()
|
||||||
|
.getPhysicalColumnName( referencedEntity.getTable(), joinCol.getReferencedColumn() );
|
||||||
|
// in JPA 2 referencedColumnName is case-insensitive
|
||||||
|
if ( referencedColumn.equalsIgnoreCase( colName ) ) {
|
||||||
|
// correct join column
|
||||||
|
if ( joinCol.isNameDeferred() ) {
|
||||||
|
joinCol.linkValueUsingDefaultColumnNaming( col, referencedEntity, value );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
joinCol.linkWithValue( value );
|
||||||
|
}
|
||||||
|
joinCol.overrideFromReferencedColumnIfNecessary( col );
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !match ) {
|
||||||
|
// we can only get here if there's a dupe PK column in the @JoinColumns
|
||||||
|
throw new AnnotationException(
|
||||||
|
"An association that targets entity '" + referencedEntity.getEntityName()
|
||||||
|
+ "' from entity '" + associatedClass.getEntityName()
|
||||||
|
+ "' has no '@JoinColumn' referencing column '"+ col.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( value instanceof ToOne ) {
|
||||||
|
( (ToOne) value).setSorted( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindNonPkReference(
|
||||||
|
PersistentClass referencedEntity,
|
||||||
|
AnnotatedJoinColumn[] columns,
|
||||||
|
SimpleValue value) {
|
||||||
|
final String referencedPropertyName;
|
||||||
|
if ( value instanceof ToOne ) {
|
||||||
|
referencedPropertyName = ( (ToOne) value).getReferencedPropertyName();
|
||||||
|
}
|
||||||
|
else if ( value instanceof DependantValue ) {
|
||||||
|
final String propertyName = columns[0].getPropertyName();
|
||||||
|
if ( propertyName != null ) {
|
||||||
|
Collection collection = (Collection) referencedEntity.getRecursiveProperty( propertyName ).getValue();
|
||||||
|
referencedPropertyName = collection.getReferencedPropertyName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AnnotationException( "The '@JoinColumn' for a secondary table must reference the primary key" );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AssertionFailure( "Property ref to an unexpected Value type: " + value.getClass().getName() );
|
||||||
|
}
|
||||||
|
if ( referencedPropertyName == null ) {
|
||||||
|
throw new AssertionFailure( "No property ref found" );
|
||||||
|
}
|
||||||
|
|
||||||
|
final Property synthProp = referencedEntity.getReferencedProperty( referencedPropertyName );
|
||||||
|
if ( synthProp == null ) {
|
||||||
|
throw new AssertionFailure( "Cannot find synthetic property: "
|
||||||
|
+ referencedEntity.getEntityName() + "." + referencedPropertyName );
|
||||||
|
}
|
||||||
|
linkJoinColumnWithValueOverridingNameIfImplicit( referencedEntity, synthProp.getValue(), columns, value );
|
||||||
|
( (SortableValue) value).sortProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindImplicitColumns(
|
||||||
|
PersistentClass referencedEntity,
|
||||||
|
AnnotatedJoinColumn[] columns,
|
||||||
|
SimpleValue value) {
|
||||||
|
final List<Column> idColumns = referencedEntity instanceof JoinedSubclass
|
||||||
|
? referencedEntity.getKey().getColumns()
|
||||||
|
: referencedEntity.getIdentifier().getColumns();
|
||||||
|
for ( Column column: idColumns ) {
|
||||||
|
columns[0].linkValueUsingDefaultColumnNaming( column, referencedEntity, value);
|
||||||
|
columns[0].overrideFromReferencedColumnIfNecessary( column );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindUnownedAssociation(
|
||||||
|
AnnotatedJoinColumn[] columns,
|
||||||
|
SimpleValue value,
|
||||||
|
PersistentClass associatedClass,
|
||||||
|
String mappedByProperty) {
|
||||||
|
for ( Column column: mappedByColumns( associatedClass, mappedByProperty ) ) {
|
||||||
|
columns[0].overrideFromReferencedColumnIfNecessary( column );
|
||||||
|
columns[0].linkValueUsingAColumnCopy( column, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Column> mappedByColumns(PersistentClass associatedClass, String mappedByProperty) {
|
||||||
|
LOG.debugf( "Retrieving property %s.%s", associatedClass.getEntityName(), mappedByProperty );
|
||||||
|
final Value value = associatedClass.getRecursiveProperty( mappedByProperty ).getValue();
|
||||||
|
if ( value instanceof Collection ) {
|
||||||
|
final Value element = ((Collection) value).getElement();
|
||||||
|
if ( element == null ) {
|
||||||
|
throw new AnnotationException( "Both sides of the bidirectional association '"
|
||||||
|
+ associatedClass.getEntityName() + "." + mappedByProperty + "' specify 'mappedBy'" );
|
||||||
|
}
|
||||||
|
return element.getColumns();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return value.getColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void linkJoinColumnWithValueOverridingNameIfImplicit(
|
public static void linkJoinColumnWithValueOverridingNameIfImplicit(
|
||||||
PersistentClass referencedEntity,
|
PersistentClass referencedEntity,
|
||||||
Value value,
|
Value value,
|
||||||
AnnotatedJoinColumn[] columns,
|
AnnotatedJoinColumn[] columns,
|
||||||
SimpleValue simpleValue) {
|
SimpleValue simpleValue) {
|
||||||
List<Column> valueColumns = value.getColumns();
|
final List<Column> valueColumns = value.getColumns();
|
||||||
for ( int i = 0; i < columns.length; i++ ) {
|
for ( int i = 0; i < columns.length; i++ ) {
|
||||||
AnnotatedJoinColumn joinCol = columns[i];
|
final AnnotatedJoinColumn joinCol = columns[i];
|
||||||
Column synthCol = valueColumns.get(i);
|
final Column synthCol = valueColumns.get(i);
|
||||||
if ( joinCol.isNameDeferred() ) {
|
if ( joinCol.isNameDeferred() ) {
|
||||||
//this has to be the default value
|
//this has to be the default value
|
||||||
joinCol.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, simpleValue );
|
joinCol.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, simpleValue );
|
||||||
|
|
|
@ -10,7 +10,6 @@ import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityNotFoundException;
|
|
||||||
import jakarta.persistence.EnumType;
|
import jakarta.persistence.EnumType;
|
||||||
import jakarta.persistence.Enumerated;
|
import jakarta.persistence.Enumerated;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
|
|
Loading…
Reference in New Issue