From d3810b8f7a035a27cf9cbca75d8318a2c551d046 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 6 Aug 2021 10:25:20 +0200 Subject: [PATCH] Implement support for non-primary-key foreign keys --- .../java/org/hibernate/mapping/Component.java | 6 ++++-- .../mapping/EmbeddableMappingType.java | 5 +++++ .../internal/MappingModelCreationHelper.java | 21 +++---------------- .../DelayedCollectionInitializer.java | 4 +++- ...iLevelCascadeCollectionEmbeddableTest.java | 2 -- ...ultiLevelCascadeCollectionIdClassTest.java | 2 -- 6 files changed, 15 insertions(+), 25 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java index ce034bcd8d..32ac703b4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java @@ -533,9 +533,11 @@ public void sortProperties() { return; } final int[] originalPropertyOrder; - // We need to capture the original property order the source is a XML mapping + // We need to capture the original property order if this is an alternate unique key + // to be able to sort the other side of the foreign key accordingly + // and also if the source is a XML mapping // because XML mappings might refer to this through the defined order - if ( getBuildingContext() instanceof MappingDocument ) { + if ( isAlternateUniqueKey() || getBuildingContext() instanceof MappingDocument ) { final Object[] originalProperties = properties.toArray(); properties.sort( Comparator.comparing( Property::getName ) ); originalPropertyOrder = new int[originalProperties.length]; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java index a62aa78f8a..3cf1c7f79b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java @@ -186,6 +186,8 @@ private EmbeddableMappingType( if ( inverseMappingType.attributeMappings.isEmpty() ) { return false; } + // Reset the attribute mappings that were added in previous attempts + this.attributeMappings.clear(); int currentIndex = 0; // We copy the attributes from the inverse mappings and replace the selection mappings for ( AttributeMapping attributeMapping : inverseMappingType.attributeMappings ) { @@ -253,6 +255,9 @@ private boolean finishInitialization( int attributeIndex = 0; int columnPosition = 0; + // Reset the attribute mappings that were added in previous attempts + this.attributeMappings.clear(); + final Iterator propertyIterator = bootDescriptor.getPropertyIterator(); while ( propertyIterator.hasNext() ) { final Property bootPropertyDescriptor = propertyIterator.next(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index 7c7706762d..24e168e983 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -19,7 +19,6 @@ import org.hibernate.MappingException; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.SharedSessionContract; -import org.hibernate.boot.model.source.internal.hbm.MappingDocument; import org.hibernate.collection.internal.StandardArraySemantics; import org.hibernate.collection.internal.StandardBagSemantics; import org.hibernate.collection.internal.StandardIdentifierBagSemantics; @@ -45,7 +44,6 @@ import org.hibernate.mapping.Property; import org.hibernate.mapping.Resolvable; import org.hibernate.mapping.Selectable; -import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.Table; import org.hibernate.mapping.ToOne; import org.hibernate.mapping.Value; @@ -1032,12 +1030,7 @@ else if ( modelPart instanceof EmbeddableValuedModelPart ) { fkTarget = referencedEntityDescriptor.getIdentifierMapping(); } else { - // TODO: need some kind of virtual model part for this -// fkTarget = attributeMapping;//bootValueMapping.; - throw new NotYetImplementedFor6Exception( - "Support for non-pk foreign-keys not yet implemented: " + - bootProperty.getPersistentClass().getEntityName() + " -> " + bootProperty.getName() - ); + fkTarget = referencedEntityDescriptor.findAttributeMapping( bootValueMapping.getReferencedPropertyName() ); } if ( fkTarget instanceof BasicValuedModelPart ) { @@ -1198,27 +1191,19 @@ private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor( private static int[] getPropertyOrder(Value bootValueMapping, MappingModelCreationProcess creationProcess) { final ComponentType componentType; - final boolean fromXml; if ( bootValueMapping instanceof Collection ) { final Collection collectionBootValueMapping = (Collection) bootValueMapping; - fromXml = collectionBootValueMapping.getBuildingContext() instanceof MappingDocument; componentType = (ComponentType) collectionBootValueMapping.getKey().getType(); } else { - if ( bootValueMapping instanceof OneToMany ) { - fromXml = ( (OneToMany) bootValueMapping ).getBuildingContext() instanceof MappingDocument; - } - else { - fromXml = ( (SimpleValue) bootValueMapping ).getBuildingContext() instanceof MappingDocument; - } final EntityType entityType = (EntityType) bootValueMapping.getType(); final Type identifierOrUniqueKeyType = entityType.getIdentifierOrUniqueKeyType( creationProcess.getCreationContext().getSessionFactory() ); componentType = (ComponentType) identifierOrUniqueKeyType; } - // If the value comes from XML, we need to consider the reordering - if ( fromXml ) { + // Consider the reordering if available + if ( componentType.getOriginalPropertyOrder() != null ) { return componentType.getOriginalPropertyOrder(); } // A value that came from the annotation model is already sorted appropriately diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java index 52f602f74c..42d0982f80 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java @@ -54,7 +54,9 @@ public void resolveKey(RowProcessingState rowProcessingState) { final Object parentKey = parentAccess.getParentKey(); - if ( parentKey != null ) { + // We can only use the parent key if the key descriptor uses the primary key of the owner i.e. refersToPrimaryKey + if ( parentKey != null && collectionAttributeMapping.getKeyDescriptor().getKeyPart().getNavigableRole().equals( + collectionAttributeMapping.findContainingEntityMapping().getIdentifierMapping().getNavigableRole() ) ) { collectionKey = new CollectionKey( collectionAttributeMapping.getCollectionDescriptor(), parentKey diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionEmbeddableTest.java index db84004cb5..f4ea66c787 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionEmbeddableTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionEmbeddableTest.java @@ -30,7 +30,6 @@ import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.Jpa; -import org.hibernate.testing.orm.junit.NotImplementedYet; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.junit.jupiter.api.Test; @@ -45,7 +44,6 @@ MultiLevelCascadeCollectionEmbeddableTest.AnotherSubSubEntity.class, MultiLevelCascadeCollectionEmbeddableTest.SubSubEntity.class }) -@NotImplementedYet(reason = "NotImplementedYetException thrown in the initialization method, by NativeNonSelectQueryPlanImpl.executeUpdate()") public class MultiLevelCascadeCollectionEmbeddableTest { private boolean initialized = false; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionIdClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionIdClassTest.java index a4c84fb59b..ea0581292d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionIdClassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/multilevel/MultiLevelCascadeCollectionIdClassTest.java @@ -29,7 +29,6 @@ import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.Jpa; -import org.hibernate.testing.orm.junit.NotImplementedYet; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.jboss.logging.Logger; @@ -46,7 +45,6 @@ MultiLevelCascadeCollectionIdClassTest.AnotherSubSubEntity.class, MultiLevelCascadeCollectionIdClassTest.SubSubEntity.class }) -@NotImplementedYet(reason = "NotImplementedYetException thrown in the initialization method, by NativeNonSelectQueryPlanImpl.executeUpdate()") public class MultiLevelCascadeCollectionIdClassTest { private static final Logger log = Logger.getLogger( MultiLevelCascadeCollectionIdClassTest.class );