diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 11fc14ae73..d1c30c1f4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -15,7 +15,6 @@ import java.util.Set; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.model.domain.EmbeddableDomainType; -import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.SemanticException; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaOrder; @@ -23,10 +22,12 @@ import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.criteria.JpaQueryStructure; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.criteria.JpaSelection; +import org.hibernate.query.sqm.FetchClauseType; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmNode; +import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; @@ -47,6 +48,7 @@ import org.hibernate.spi.NavigablePath; import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.metamodel.SingularAttribute; /** * Defines the commonality between a root query and a subquery. @@ -519,6 +521,10 @@ public class SqmQuerySpec extends SqmQueryPart collectSelectedFromSet( selectedFromSet, (SqmFrom) path.getLhs() ); } } + else if ( selectableNode instanceof SqmEmbeddedValuedSimplePath ) { + final SqmEmbeddedValuedSimplePath path = (SqmEmbeddedValuedSimplePath) selectableNode; + assertEmbeddableCollections( path.getNavigablePath(), (EmbeddableDomainType) path.getSqmType() ); + } } private void collectSelectedFromSet(Set> selectedFromSet, SqmFrom sqmFrom) { @@ -569,6 +575,22 @@ public class SqmQuerySpec extends SqmQueryPart } } + private void assertEmbeddableCollections(NavigablePath navigablePath, EmbeddableDomainType embeddableType) { + if ( !embeddableType.getPluralAttributes().isEmpty() ) { + throw new SemanticException( String.format( + "Explicit selection of an embeddable containing associated collections is not supported: %s", + navigablePath + ) ); + } + else { + for ( SingularAttribute attribute : embeddableType.getSingularAttributes() ) { + if ( attribute.getType() instanceof EmbeddableDomainType ) { + assertEmbeddableCollections( navigablePath, (EmbeddableDomainType) attribute.getType() ); + } + } + } + } + @Override public void appendHqlString(StringBuilder sb) { if ( selectClause != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java index fd72b55b8b..2db14d056e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java @@ -6,10 +6,8 @@ */ package org.hibernate.sql.results.graph.embeddable.internal; -import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; -import org.hibernate.query.SemanticException; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.FromClauseAccess; @@ -47,10 +45,6 @@ public class EmbeddableResultImpl extends AbstractFetchParent implements Embe this.fetchContainer = modelPart.getEmbeddableTypeDescriptor(); this.resultVariable = resultVariable; - // We currently don't support explicitly selecting embeddables that contain collections - // as we wouldn't be able to correctly initialize their owning entity instances - checkContainsCollections( modelPart, navigablePath ); - /* An `{embeddable_result}` sub-path is created for the corresponding initializer to differentiate it from a fetch-initializer if this embedded is also fetched. The Jakarta Persistence spec says that any embedded value selected in the result should not be part of the state of any managed entity. @@ -96,25 +90,6 @@ public class EmbeddableResultImpl extends AbstractFetchParent implements Embe return false; } - private static void checkContainsCollections( - EmbeddableValuedModelPart embeddableModelPart, - NavigablePath navigablePath) { - embeddableModelPart.forEachSubPart( (index, modelPart) -> { - final AttributeMapping attribute = modelPart.asAttributeMapping(); - if ( attribute != null ) { - if ( attribute.isPluralAttributeMapping() ) { - throw new SemanticException( String.format( - "Explicit selection of an embeddable containing collections is not supported: %s", - navigablePath - ) ); - } - else if ( attribute.isEmbeddedAttributeMapping() ) { - checkContainsCollections( attribute.asEmbeddedAttributeMapping(), navigablePath ); - } - } - } ); - } - @Override public String getResultVariable() { return resultVariable;