HHH-16997 Embedded components in HibernateProxy are not initialized when entity has reference to another entity of the same type + HHH-16901 Embedded field in entity association from composite key not correctly instantiated

This commit is contained in:
Andrea Boriero 2023-08-01 15:08:58 +02:00 committed by Christian Beikov
parent 7cb00f3521
commit 22535bbd92
16 changed files with 79 additions and 27 deletions

View File

@ -170,7 +170,7 @@ public abstract class AbstractPluralAttribute<D, C, E>
"`lhs` cannot be null for a sub-navigable reference - " + getName()
);
}
final SqmPathSource<?> parentPathSource = parent.getReferencedPathSource();
final SqmPathSource<?> parentPathSource = parent.getResolvedModel();
NavigablePath navigablePath = parent.getNavigablePath();
if ( parentPathSource instanceof PluralPersistentAttribute<?, ?, ?> ) {
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );

View File

@ -7,6 +7,7 @@
package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
@ -47,7 +48,9 @@ public class EmbeddedSqmPathSource<J>
@Override
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
return new SqmEmbeddedValuedSimplePath<>(
PathHelper.append( lhs, this, intermediatePathSource ),
pathModel instanceof SqmJoinable<?, ?>
? ( (SqmJoinable<?, ?>) pathModel ).createNavigablePath( lhs, null )
: PathHelper.append( lhs, this, intermediatePathSource ),
pathModel,
lhs,
lhs.nodeBuilder()

View File

@ -173,7 +173,7 @@ public class SingularAttributeImpl<D,J>
"LHS cannot be null for a sub-navigable reference - " + getName()
);
}
final SqmPathSource<?> parentPathSource = parent.getReferencedPathSource();
final SqmPathSource<?> parentPathSource = parent.getResolvedModel();
NavigablePath navigablePath = parent.getNavigablePath();
if ( parentPathSource instanceof PluralPersistentAttribute<?, ?, ?> ) {
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );
@ -224,7 +224,7 @@ public class SingularAttributeImpl<D,J>
);
}
NavigablePath navigablePath = parent.getNavigablePath();
if ( parent.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?> ) {
if ( parent.getResolvedModel() instanceof PluralPersistentAttribute<?, ?, ?> ) {
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );
}
if ( getDeclaringType() instanceof IdentifiableDomainType<?> ) {

View File

@ -53,7 +53,7 @@ public class SqmCreationHelper {
);
}
NavigablePath navigablePath = lhs.getNavigablePath();
if ( lhs.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?>
if ( lhs.getResolvedModel() instanceof PluralPersistentAttribute<?, ?, ?>
&& CollectionPart.Nature.fromName( subNavigable ) == null ) {
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );
}

View File

@ -5049,7 +5049,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return expression;
}
final BasicValuedPathInterpretation<?> basicPath = (BasicValuedPathInterpretation<?>) expression;
final AbstractEntityPersister persister = (AbstractEntityPersister) basicPath.getTableGroup().getModelPart().getPartMappingType();
final TableGroup tableGroup = basicPath.getTableGroup();
final TableGroup elementTableGroup = tableGroup instanceof PluralTableGroup
? ( (PluralTableGroup) tableGroup ).getElementTableGroup()
: tableGroup;
final AbstractEntityPersister persister = (AbstractEntityPersister) elementTableGroup.getModelPart().getPartMappingType();
// Only need a case expression around the basic valued path for the parent treat expression
// if the column of the basic valued path is shared between subclasses
if ( persister.isSharedColumn( basicPath.getColumnReference().getColumnExpression() ) ) {

View File

@ -103,6 +103,11 @@ public class SqmTreatedBagJoin<O,T, S extends T> extends SqmBagJoin<O,S> impleme
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public SqmAttributeJoin<O, S> makeCopy(SqmCreationProcessingState creationProcessingState) {
return new SqmTreatedBagJoin<>( wrappedPath, treatTarget, getAlias() );

View File

@ -91,8 +91,12 @@ public class SqmTreatedCrossJoin<T, S extends T> extends SqmCrossJoin<S> impleme
@Override
public EntityDomainType<S> getReferencedPathSource() {
//noinspection unchecked
return (EntityDomainType<S>) wrappedPath.getReferencedPathSource();
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override

View File

@ -77,6 +77,11 @@ public class SqmTreatedEntityJoin<T, S extends T> extends SqmEntityJoin<S> imple
return treatTarget;
}
@Override
public EntityDomainType<S> getModel() {
return getTreatTarget();
}
@Override
public SqmPath<T> getWrappedPath() {
return wrappedPath;

View File

@ -102,6 +102,11 @@ public class SqmTreatedListJoin<O,T, S extends T> extends SqmListJoin<O,S> imple
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public SqmPath<?> resolveIndexedAccess(
SqmExpression<?> selector,

View File

@ -98,6 +98,11 @@ public class SqmTreatedMapJoin<O, K, V, S extends V> extends SqmMapJoin<O, K, S>
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public SqmMapJoin<O, K, S> makeCopy(SqmCreationProcessingState creationProcessingState) {
return new SqmTreatedMapJoin<>(

View File

@ -91,6 +91,16 @@ public class SqmTreatedPluralPartJoin<O,T, S extends T> extends SqmPluralPartJoi
return treatTarget;
}
@Override
public SqmPathSource<S> getReferencedPathSource() {
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "treat(" );

View File

@ -100,6 +100,11 @@ public class SqmTreatedSetJoin<O,T, S extends T> extends SqmSetJoin<O,S> impleme
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public SqmAttributeJoin<O, S> makeCopy(SqmCreationProcessingState creationProcessingState) {
return new SqmTreatedSetJoin<>( wrappedPath, treatTarget, getAlias() );

View File

@ -109,6 +109,16 @@ public class SqmTreatedSimplePath<T, S extends T>
return treatTarget;
}
@Override
public SqmPathSource<S> getReferencedPathSource() {
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override
public <S1 extends S> SqmTreatedSimplePath<S,S1> treatAs(Class<S1> treatJavaType) throws PathException {
return super.treatAs( treatJavaType );

View File

@ -97,7 +97,12 @@ public class SqmTreatedSingularJoin<O,T, S extends T> extends SqmSingularJoin<O,
@Override
public EntityDomainType<S> getReferencedPathSource() {
return getTreatTarget();
return treatTarget;
}
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override

View File

@ -8,13 +8,14 @@ package org.hibernate.sql.results.graph.embeddable;
import org.hibernate.engine.internal.ManagedTypeHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.VirtualModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedIdentifierMappingImpl;
import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.proxy.HibernateProxy;
@ -68,31 +69,20 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
final int size = embeddableTypeDescriptor.getNumberOfFetchables();
this.rowState = new Object[ size ];
this.isPartOfKey = isPartOfKey( embedded, navigablePath );
this.isPartOfKey = isPartOfKey( embedded, navigablePath, fetchParentAccess );
// We never want to create empty composites for the FK target or PK, otherwise collections would break
this.createEmptyCompositesEnabled = !isPartOfKey && embeddableTypeDescriptor.isCreateEmptyCompositesEnabled();
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
this.assemblers = createAssemblers( resultDescriptor, creationState, embeddableTypeDescriptor );
}
private static boolean isPartOfKey(EmbeddableValuedModelPart modelPart, NavigablePath navigablePath) {
private static boolean isPartOfKey(EmbeddableValuedModelPart modelPart, NavigablePath navigablePath, FetchParentAccess fetchParentAccess) {
return modelPart.isEntityIdentifierMapping()
|| isPartOfKey( navigablePath )
|| modelPart.getNavigableRole() != null && isPartOfKey( modelPart.getNavigableRole() );
}
private static boolean isPartOfKey(NavigablePath navigablePath) {
return ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
|| ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
|| ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() )
|| navigablePath instanceof EntityIdentifierNavigablePath
|| navigablePath.getParent().getParent() != null && isPartOfKey( navigablePath.getParent() );
}
private static boolean isPartOfKey(NavigableRole navigableRole) {
final NavigableRole parent = navigableRole.getParent();
return parent != null
&& ( parent.getLocalName().equals( EntityIdentifierMapping.ID_ROLE_NAME ) || isPartOfKey( parent ) );
|| fetchParentAccess != null && fetchParentAccess.isEmbeddableInitializer()
&& ( (AbstractEmbeddableInitializer) fetchParentAccess ).isPartOfKey;
}
protected DomainResultAssembler<?>[] createAssemblers(

View File

@ -74,6 +74,7 @@ public class EmbeddableAscDescQueryTest {
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createMutationQuery( "update EntityA a set a.componentA.entityA = null" ).executeUpdate();
session.createMutationQuery( "delete from EntityB" ).executeUpdate();
session.createMutationQuery( "delete from EntityC" ).executeUpdate();
session.createMutationQuery( "delete from EntityA" ).executeUpdate();