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 1adfe41aa1
commit ef05e99c7e
16 changed files with 80 additions and 27 deletions

View File

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

View File

@ -7,6 +7,7 @@
package org.hibernate.metamodel.model.domain.internal; package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType; import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
@ -49,7 +50,10 @@ public class EmbeddedSqmPathSource<J>
@Override @Override
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) { public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
final NavigablePath navigablePath; final NavigablePath navigablePath;
if ( intermediatePathSource == null ) { if ( pathModel instanceof SqmJoinable<?, ?> ) {
navigablePath = ( (SqmJoinable<?, ?>) pathModel ).createNavigablePath( lhs, null );
}
else if ( intermediatePathSource == null ) {
navigablePath = lhs.getNavigablePath().append( getPathName() ); navigablePath = lhs.getNavigablePath().append( getPathName() );
} }
else { else {

View File

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

View File

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

View File

@ -5001,7 +5001,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return expression; return expression;
} }
final BasicValuedPathInterpretation<?> basicPath = (BasicValuedPathInterpretation<?>) 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 // 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 the column of the basic valued path is shared between subclasses
if ( persister.isSharedColumn( basicPath.getColumnReference().getColumnExpression() ) ) { 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; return treatTarget;
} }
@Override
public SqmPathSource<?> getResolvedModel() {
return treatTarget;
}
@Override @Override
public SqmAttributeJoin<O, S> makeCopy(SqmCreationProcessingState creationProcessingState) { public SqmAttributeJoin<O, S> makeCopy(SqmCreationProcessingState creationProcessingState) {
return new SqmTreatedBagJoin<>( wrappedPath, treatTarget, getAlias() ); return new SqmTreatedBagJoin<>( wrappedPath, treatTarget, getAlias() );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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