diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java index a7115a5c06..593d19e2b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableValuedModelPart.java @@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping; import java.util.List; +import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.SqlAstCreationState; @@ -46,7 +47,9 @@ public interface EmbeddableValuedModelPart extends ModelPart, Fetchable, Fetchab /** * @see org.hibernate.annotations.Parent */ - SingularAttributeMapping getParentInjectionAttributeMapping(); + default PropertyAccess getParentInjectionAttributePropertyAccess() { + return null; + } Expression toSqlExpression( TableGroup tableGroup, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 859a336948..f4ebf444d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -22,9 +22,9 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.model.domain.NavigableRole; +import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; @@ -62,6 +62,7 @@ public class EmbeddedAttributeMapping private final String tableExpression; private final String[] attrColumnNames; private final EmbeddableMappingType embeddableMappingType; + private final PropertyAccess parentInjectionAttributeProperyAccess; @SuppressWarnings("WeakerAccess") public EmbeddedAttributeMapping( @@ -71,6 +72,7 @@ public class EmbeddedAttributeMapping String tableExpression, String[] attrColumnNames, StateArrayContributorMetadataAccess attributeMetadataAccess, + String parentInjectionAttributeName, FetchStrategy mappedFetchStrategy, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, @@ -83,6 +85,15 @@ public class EmbeddedAttributeMapping declaringType, propertyAccess ); + if ( parentInjectionAttributeName != null ) { + parentInjectionAttributeProperyAccess = PropertyAccessStrategyBasicImpl.INSTANCE.buildPropertyAccess( + embeddableMappingType.getMappedJavaTypeDescriptor().getJavaType(), + parentInjectionAttributeName + ); + } + else { + parentInjectionAttributeProperyAccess = null; + } this.navigableRole = navigableRole; this.tableExpression = tableExpression; this.attrColumnNames = attrColumnNames; @@ -99,12 +110,6 @@ public class EmbeddedAttributeMapping return embeddableMappingType; } - @Override - public SingularAttributeMapping getParentInjectionAttributeMapping() { - // todo (6.0) : implement - return null; - } - @Override public String getContainingTableExpression() { return tableExpression; @@ -115,6 +120,11 @@ public class EmbeddedAttributeMapping return Arrays.asList( attrColumnNames ); } + @Override + public PropertyAccess getParentInjectionAttributePropertyAccess() { + return parentInjectionAttributeProperyAccess; + } + @Override public void visitJdbcTypes( Consumer action, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java index 894e7f906e..68149d6b38 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -22,9 +22,10 @@ import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl; +import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.sql.ast.Clause; @@ -59,7 +60,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF private final String containingTableExpression; - private final SingularAttributeMapping parentInjectionAttribute; + private final PropertyAccess parentInjectionAttributeProperyAccess; private final List columnExpressions; private final String sqlAliasStem; @@ -68,15 +69,24 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF CollectionPersister collectionDescriptor, Nature nature, EmbeddableMappingType embeddableMappingType, - SingularAttributeMapping parentInjectionAttribute, + String parentInjectionAttributeName, String containingTableExpression, List columnExpressions, String sqlAliasStem) { this.navigableRole = collectionDescriptor.getNavigableRole().appendContainer( nature.getName() ); this.collectionDescriptor = collectionDescriptor; this.nature = nature; + if ( parentInjectionAttributeName != null ) { + parentInjectionAttributeProperyAccess = PropertyAccessStrategyBasicImpl.INSTANCE.buildPropertyAccess( + embeddableMappingType.getMappedJavaTypeDescriptor().getJavaType(), + parentInjectionAttributeName + ); + } + else { + parentInjectionAttributeProperyAccess = null; + } this.embeddableMappingType = embeddableMappingType; - this.parentInjectionAttribute = parentInjectionAttribute; + this.containingTableExpression = containingTableExpression; this.columnExpressions = columnExpressions; this.sqlAliasStem = sqlAliasStem; @@ -108,8 +118,8 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF } @Override - public SingularAttributeMapping getParentInjectionAttributeMapping() { - return parentInjectionAttribute; + public PropertyAccess getParentInjectionAttributePropertyAccess() { + return parentInjectionAttributeProperyAccess; } @Override 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 41dc14ace6..83279380a4 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 @@ -413,8 +413,9 @@ public class MappingModelCreationHelper { creationProcess ); + final Component component = (Component) bootProperty.getValue(); final EmbeddableMappingType embeddableMappingType = EmbeddableMappingType.from( - (Component) bootProperty.getValue(), + component, attrType, attributeMappingType -> new EmbeddedAttributeMapping( attrName, @@ -423,6 +424,7 @@ public class MappingModelCreationHelper { tableExpression, attrColumnNames, attributeMetadataAccess, + component.getParentProperty(), FetchStrategy.IMMEDIATE_JOIN, attributeMappingType, declaringType, @@ -1115,7 +1117,7 @@ public class MappingModelCreationHelper { CollectionPart.Nature.INDEX, inflightDescriptor, // parent-injection - null, + component.getParentProperty(), tableExpression, columnExpressions, sqlAliasStem @@ -1206,7 +1208,7 @@ public class MappingModelCreationHelper { CollectionPart.Nature.ELEMENT, embeddableMappingType, // parent-injection - null, + component.getParentProperty(), tableExpression, columnExpressions, sqlAliasStem diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java index a3e7618196..eaf86e5a57 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java @@ -9,16 +9,20 @@ package org.hibernate.sql.results.graph.embeddable; import java.util.IdentityHashMap; import java.util.Map; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; -import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMapping; +import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.graph.AbstractFetchParentAccess; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParentAccess; +import org.hibernate.sql.results.graph.Initializer; +import org.hibernate.sql.results.graph.collection.CollectionInitializer; +import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; @@ -28,7 +32,7 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState; public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentAccess implements EmbeddableInitializer { private final NavigablePath navigablePath; private final EmbeddableValuedModelPart embeddedModelPartDescriptor; - private final FetchParentAccess fetchParentAccess; + private FetchParentAccess fetchParentAccess; private final Map assemblerMap; @@ -92,26 +96,27 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA // todo (6.0) : register "parent resolution listener" if the composite is defined for `@Parent` // something like: - final SingularAttributeMapping parentInjectionTarget = embeddedModelPartDescriptor.getParentInjectionAttributeMapping(); + final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess(); - if ( parentInjectionTarget != null ) { - getFetchParentAccess().findFirstEntityDescriptorAccess().registerResolutionListener( - // todo (6.0) : this is the legacy behavior - // - the first entity is injected as the parent, even if the composite - // is defined on another composite - owner -> { - if ( compositeInstance == null ) { - return; + if ( parentInjectionPropertyAccess != null ) { + if ( getFetchParentAccess() != null ) { + getFetchParentAccess().findFirstEntityDescriptorAccess().registerResolutionListener( + // todo (6.0) : this is the legacy behavior + // - the first entity is injected as the parent, even if the composite + // is defined on another composite + owner -> { + if ( compositeInstance == null ) { + return; + } + parentInjectionPropertyAccess.getSetter().set( + compositeInstance, + owner, + rowProcessingState.getSession().getFactory() + ); } - parentInjectionTarget.getPropertyAccess().getSetter().set( - compositeInstance, - owner, - rowProcessingState.getSession().getFactory() - ); - } - ); + ); + } } - } @Override @@ -130,6 +135,33 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA @Override public void initializeInstance(RowProcessingState rowProcessingState) { + final PropertyAccess parentInjectionPropertyAccess = embeddedModelPartDescriptor.getParentInjectionAttributePropertyAccess(); + + if ( parentInjectionPropertyAccess != null && getFetchParentAccess() == null ) { + Initializer initializer = rowProcessingState.resolveInitializer( navigablePath.getParent() ); + final Object owner; + if ( initializer instanceof CollectionInitializer ) { + owner = ( (CollectionInitializer) initializer ).getCollectionInstance().getOwner(); + } + else if ( initializer instanceof EntityInitializer ) { + owner = ( (EntityInitializer) initializer ).getEntityInstance(); + + parentInjectionPropertyAccess.getSetter().set( + compositeInstance, + owner, + rowProcessingState.getSession().getFactory() + ); + } + else { + throw new NotYetImplementedFor6Exception( getClass() ); + } + parentInjectionPropertyAccess.getSetter().set( + compositeInstance, + owner, + rowProcessingState.getSession().getFactory() + ); + } + EmbeddableLoadingLogger.INSTANCE.debugf( "Initializing composite instance [%s] : %s", navigablePath, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/annotations/embedded/EmbeddedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/annotations/embedded/EmbeddedTest.java index 86accdbe91..a2d57c8920 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/annotations/embedded/EmbeddedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bootstrap/binding/annotations/embedded/EmbeddedTest.java @@ -484,7 +484,6 @@ public class EmbeddedTest { } @Test - @FailureExpected(reason = "@Parent annotation mapping has not yet been implemented") public void testParent(SessionFactoryScope scope) { Book book = new Book(); scope.inTransaction(