introduce ForeignKeyType and AnnotationJoinColumns.getReferencedColumnsType()
This commit is contained in:
parent
849246e3cd
commit
e2f42cd0bc
|
@ -406,9 +406,7 @@ public class AnnotatedColumn {
|
|||
}
|
||||
else {
|
||||
final Table table = value.getTable();
|
||||
if ( getParent() != null ) {
|
||||
getParent().setTableInternal( table );
|
||||
}
|
||||
getParent().setTableInternal( table );
|
||||
getMappingColumn().setValue( value );
|
||||
value.addColumn( getMappingColumn(), insertable, updatable );
|
||||
table.addColumn( getMappingColumn() );
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
*/
|
||||
package org.hibernate.cfg;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
|
@ -21,14 +19,10 @@ import org.hibernate.boot.model.relational.Database;
|
|||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.Value;
|
||||
|
||||
import static org.hibernate.cfg.BinderHelper.findReferencedColumnOwner;
|
||||
import static org.hibernate.cfg.BinderHelper.getRelativePath;
|
||||
import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue;
|
||||
import static org.hibernate.cfg.BinderHelper.isEmptyOrNullAnnotationValue;
|
||||
|
@ -377,80 +371,6 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
|
|||
}
|
||||
}
|
||||
|
||||
//keep it JDK 1.4 compliant
|
||||
//implicit way
|
||||
public static final int NO_REFERENCE = 0;
|
||||
//reference to the pk in an explicit order
|
||||
public static final int PK_REFERENCE = 1;
|
||||
//reference to non pk columns
|
||||
public static final int NON_PK_REFERENCE = 2;
|
||||
|
||||
public static int checkReferencedColumnsType(
|
||||
AnnotatedJoinColumns joinColumns,
|
||||
PersistentClass referencedEntity,
|
||||
MetadataBuildingContext context) {
|
||||
final List<AnnotatedJoinColumn> columns = joinColumns.getJoinColumns();
|
||||
if ( columns.size() == 0 ) {
|
||||
return NO_REFERENCE; //shortcut
|
||||
}
|
||||
|
||||
final AnnotatedJoinColumn firstColumn = columns.get(0);
|
||||
final Object columnOwner = findReferencedColumnOwner( referencedEntity, firstColumn, context );
|
||||
if ( columnOwner == null ) {
|
||||
try {
|
||||
throw new MappingException( "A '@JoinColumn' references a column named '"
|
||||
+ firstColumn.getReferencedColumn() + "' but the target entity '"
|
||||
+ referencedEntity.getEntityName() + "' has no property which maps to this column" );
|
||||
}
|
||||
catch (MappingException me) {
|
||||
// we throw a recoverable exception here in case this
|
||||
// is merely an ordering issue, so that the SecondPass
|
||||
// will get reprocessed later
|
||||
throw new RecoverableException( me.getMessage(), me );
|
||||
}
|
||||
}
|
||||
final Table table = getTable( columnOwner );
|
||||
final List<Selectable> keyColumns = referencedEntity.getKey().getSelectables();
|
||||
boolean explicitColumnReference = false;
|
||||
for ( AnnotatedJoinColumn column : columns ) {
|
||||
if ( !column.isReferenceImplicit() ) {
|
||||
explicitColumnReference = true;
|
||||
if ( !keyColumns.contains( column( context, table, column.getReferencedColumn() ) ) ) {
|
||||
// we have a column which does not belong to the PK
|
||||
return NON_PK_REFERENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( explicitColumnReference ) {
|
||||
// if we got to here, all the columns belong to the PK
|
||||
return keyColumns.size() == columns.size()
|
||||
// we have all the PK columns
|
||||
? PK_REFERENCE
|
||||
// we have a subset of the PK columns
|
||||
: NON_PK_REFERENCE;
|
||||
}
|
||||
else {
|
||||
// there were no nonempty referencedColumnNames
|
||||
return NO_REFERENCE;
|
||||
}
|
||||
}
|
||||
|
||||
private static Table getTable(Object persistentClassOrJoin) {
|
||||
return persistentClassOrJoin instanceof PersistentClass
|
||||
? ( (PersistentClass) persistentClassOrJoin ).getTable()
|
||||
: ( (Join) persistentClassOrJoin ).getTable();
|
||||
}
|
||||
|
||||
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() + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to apply column definitions from the referenced FK column to this column.
|
||||
*
|
||||
|
|
|
@ -3,10 +3,10 @@ package org.hibernate.cfg;
|
|||
import jakarta.persistence.JoinColumn;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.JoinColumnOrFormula;
|
||||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.boot.model.naming.EntityNaming;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.naming.ImplicitJoinColumnNameSource;
|
||||
|
@ -25,12 +25,14 @@ import org.hibernate.mapping.PersistentClass;
|
|||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hibernate.cfg.BinderHelper.findReferencedColumnOwner;
|
||||
import static org.hibernate.cfg.BinderHelper.getRelativePath;
|
||||
import static org.hibernate.cfg.BinderHelper.isEmptyOrNullAnnotationValue;
|
||||
import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder;
|
||||
|
@ -238,27 +240,6 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
|
|||
return mappedByTableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override persistent class on oneToMany Cases for late settings
|
||||
* Must only be used on second level pass binding
|
||||
*/
|
||||
public void setPersistentClass(
|
||||
PersistentClass persistentClass,
|
||||
Map<String, Join> joins,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass) {
|
||||
// TODO shouldn't we deduce the class name from the persistentClass?
|
||||
final PropertyHolder propertyHolder = buildPropertyHolder(
|
||||
persistentClass,
|
||||
joins,
|
||||
getBuildingContext(),
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
setPropertyHolder( propertyHolder );
|
||||
// for ( AnnotatedJoinColumn column : columns ) {
|
||||
// column.setPropertyHolder( propertyHolder );
|
||||
// }
|
||||
}
|
||||
|
||||
public boolean isElementCollection() {
|
||||
return elementCollection;
|
||||
}
|
||||
|
@ -281,6 +262,74 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
|
|||
mappedByPropertyName = mappedByProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given {@link AnnotatedJoinColumns} represent a reference to
|
||||
* the primary key of the given {@link PersistentClass}, or whether they reference
|
||||
* some other combination of mapped columns.
|
||||
*/
|
||||
public ForeignKeyType getReferencedColumnsType(
|
||||
PersistentClass referencedEntity) {
|
||||
if ( columns.isEmpty() ) {
|
||||
return ForeignKeyType.IMPLICIT_PRIMARY_KEY_REFERENCE; //shortcut
|
||||
}
|
||||
|
||||
final AnnotatedJoinColumn firstColumn = columns.get(0);
|
||||
final Object columnOwner = findReferencedColumnOwner( referencedEntity, firstColumn, getBuildingContext() );
|
||||
if ( columnOwner == null ) {
|
||||
try {
|
||||
throw new MappingException( "A '@JoinColumn' references a column named '"
|
||||
+ firstColumn.getReferencedColumn() + "' but the target entity '"
|
||||
+ referencedEntity.getEntityName() + "' has no property which maps to this column" );
|
||||
}
|
||||
catch (MappingException me) {
|
||||
// we throw a recoverable exception here in case this
|
||||
// is merely an ordering issue, so that the SecondPass
|
||||
// will get reprocessed later
|
||||
throw new RecoverableException( me.getMessage(), me );
|
||||
}
|
||||
}
|
||||
final Table table = table( columnOwner );
|
||||
final List<Selectable> keyColumns = referencedEntity.getKey().getSelectables();
|
||||
boolean explicitColumnReference = false;
|
||||
for ( AnnotatedJoinColumn column : columns ) {
|
||||
if ( !column.isReferenceImplicit() ) {
|
||||
explicitColumnReference = true;
|
||||
if ( !keyColumns.contains( column( getBuildingContext(), table, column.getReferencedColumn() ) ) ) {
|
||||
// we have a column which does not belong to the PK
|
||||
return ForeignKeyType.NON_PRIMARY_KEY_REFERENCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( explicitColumnReference ) {
|
||||
// if we got to here, all the columns belong to the PK
|
||||
return keyColumns.size() == columns.size()
|
||||
// we have all the PK columns
|
||||
? ForeignKeyType.EXPLICIT_PRIMARY_KEY_REFERENCE
|
||||
// we have a subset of the PK columns
|
||||
: ForeignKeyType.NON_PRIMARY_KEY_REFERENCE;
|
||||
}
|
||||
else {
|
||||
// there were no nonempty referencedColumnNames
|
||||
return ForeignKeyType.IMPLICIT_PRIMARY_KEY_REFERENCE;
|
||||
}
|
||||
}
|
||||
|
||||
private static Table table(Object persistentClassOrJoin) {
|
||||
return persistentClassOrJoin instanceof PersistentClass
|
||||
? ( (PersistentClass) persistentClassOrJoin ).getTable()
|
||||
: ( (Join) persistentClassOrJoin ).getTable();
|
||||
}
|
||||
|
||||
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() + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
String buildDefaultColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
|
||||
final MetadataBuildingOptions options = getBuildingContext().getBuildingOptions();
|
||||
final ImplicitNamingStrategy implicitNamingStrategy = options.getImplicitNamingStrategy();
|
||||
|
|
|
@ -81,8 +81,6 @@ import jakarta.persistence.UniqueConstraint;
|
|||
import static org.hibernate.cfg.AnnotatedColumn.buildColumnOrFormulaFromAnnotation;
|
||||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||
|
||||
import static org.hibernate.cfg.AnnotatedJoinColumn.NON_PK_REFERENCE;
|
||||
import static org.hibernate.cfg.AnnotatedJoinColumn.checkReferencedColumnsType;
|
||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
|
||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP;
|
||||
|
@ -173,7 +171,7 @@ public class BinderHelper {
|
|||
boolean inverse,
|
||||
MetadataBuildingContext context) {
|
||||
// this work is not necessary for a primary key reference
|
||||
if ( checkReferencedColumnsType( joinColumns, targetEntity, context ) == NON_PK_REFERENCE ) { // && !firstColumn.isImplicit()
|
||||
if ( joinColumns.getReferencedColumnsType( targetEntity ) == ForeignKeyType.NON_PRIMARY_KEY_REFERENCE ) { // && !firstColumn.isImplicit()
|
||||
// all the columns have to belong to the same table;
|
||||
// figure out which table has the columns by looking
|
||||
// for a PersistentClass or Join in the hierarchy of
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.cfg;
|
||||
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
|
||||
/**
|
||||
* Specifies the sort of foreign key reference given in a
|
||||
* {@link jakarta.persistence.JoinColumn} annotation.
|
||||
*
|
||||
* @see AnnotatedJoinColumns#getReferencedColumnsType(PersistentClass)
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public enum ForeignKeyType {
|
||||
EXPLICIT_PRIMARY_KEY_REFERENCE,
|
||||
IMPLICIT_PRIMARY_KEY_REFERENCE,
|
||||
NON_PRIMARY_KEY_REFERENCE
|
||||
}
|
|
@ -79,7 +79,8 @@ public final class PropertyHolderBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* must only be used on second level phases (<join> has to be settled already)
|
||||
* May only be called during the second pass phase.
|
||||
* (The join must have already been set.)
|
||||
*/
|
||||
public static PropertyHolder buildPropertyHolder(
|
||||
PersistentClass persistentClass,
|
||||
|
|
|
@ -167,6 +167,7 @@ import static org.hibernate.cfg.BinderHelper.getPath;
|
|||
import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue;
|
||||
import static org.hibernate.cfg.BinderHelper.toAliasEntityMap;
|
||||
import static org.hibernate.cfg.BinderHelper.toAliasTableMap;
|
||||
import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder;
|
||||
import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromExternalName;
|
||||
import static org.hibernate.internal.util.StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||
|
@ -1538,8 +1539,13 @@ public abstract class CollectionBinder {
|
|||
oneToMany.setAssociatedClass( associatedClass );
|
||||
|
||||
final Map<String, Join> joins = buildingContext.getMetadataCollector().getJoins( referencedEntityName );
|
||||
foreignJoinColumns.setPersistentClass( associatedClass, joins, inheritanceStatePerClass );
|
||||
foreignJoinColumns.setJoins( joins );
|
||||
foreignJoinColumns.setPropertyHolder( buildPropertyHolder(
|
||||
associatedClass,
|
||||
joins,
|
||||
foreignJoinColumns.getBuildingContext(),
|
||||
inheritanceStatePerClass
|
||||
) );
|
||||
foreignJoinColumns.setJoins( joins);
|
||||
collection.setCollectionTable( foreignJoinColumns.getTable() );
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf( "Mapping collection: %s -> %s", collection.getRole(), collection.getCollectionTable().getName() );
|
||||
|
|
|
@ -49,9 +49,6 @@ import org.hibernate.mapping.Value;
|
|||
|
||||
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.internal.util.StringHelper.isNotEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.isQuoted;
|
||||
|
@ -574,15 +571,16 @@ public class TableBinder {
|
|||
SimpleValue value,
|
||||
MetadataBuildingContext buildingContext,
|
||||
PersistentClass associatedClass) {
|
||||
switch ( checkReferencedColumnsType( joinColumns, referencedEntity, buildingContext ) ) {
|
||||
case NON_PK_REFERENCE:
|
||||
switch ( joinColumns.getReferencedColumnsType( referencedEntity ) ) {
|
||||
case NON_PRIMARY_KEY_REFERENCE:
|
||||
bindNonPrimaryKeyReference( referencedEntity, joinColumns, value );
|
||||
break;
|
||||
case NO_REFERENCE:
|
||||
case IMPLICIT_PRIMARY_KEY_REFERENCE:
|
||||
bindImplicitPrimaryKeyReference( referencedEntity, joinColumns, value, associatedClass );
|
||||
break;
|
||||
default:
|
||||
case EXPLICIT_PRIMARY_KEY_REFERENCE:
|
||||
bindPrimaryKeyReference( referencedEntity, joinColumns, value, associatedClass, buildingContext );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,20 +757,20 @@ public class TableBinder {
|
|||
}
|
||||
}
|
||||
|
||||
public static void addIndexes(Table hibTable, org.hibernate.annotations.Index[] indexes, MetadataBuildingContext buildingContext) {
|
||||
public static void addIndexes(Table table, org.hibernate.annotations.Index[] indexes, MetadataBuildingContext context) {
|
||||
for ( org.hibernate.annotations.Index index : indexes ) {
|
||||
//no need to handle inSecondPass here since it is only called from EntityBinder
|
||||
buildingContext.getMetadataCollector().addSecondPass(
|
||||
new IndexOrUniqueKeySecondPass( hibTable, index.name(), index.columnNames(), buildingContext )
|
||||
context.getMetadataCollector().addSecondPass(
|
||||
new IndexOrUniqueKeySecondPass( table, index.name(), index.columnNames(), context )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addIndexes(Table hibTable, Index[] indexes, MetadataBuildingContext buildingContext) {
|
||||
buildingContext.getMetadataCollector().addJpaIndexHolders( hibTable, buildJpaIndexHolder( indexes ) );
|
||||
public static void addIndexes(Table table, Index[] indexes, MetadataBuildingContext context) {
|
||||
context.getMetadataCollector().addJpaIndexHolders( table, buildJpaIndexHolder( indexes ) );
|
||||
}
|
||||
|
||||
public static List<JPAIndexHolder> buildJpaIndexHolder(Index[] indexes){
|
||||
public static List<JPAIndexHolder> buildJpaIndexHolder(Index[] indexes) {
|
||||
List<JPAIndexHolder> holders = new ArrayList<>( indexes.length );
|
||||
for ( Index index : indexes ) {
|
||||
holders.add( new JPAIndexHolder( index ) );
|
||||
|
|
Loading…
Reference in New Issue