HHH-17572 Move embedded collections selection check to query validation

This commit is contained in:
Marco Belladelli 2023-12-18 11:28:57 +01:00 committed by Christian Beikov
parent f88ef3e7c2
commit b9e230e456
2 changed files with 23 additions and 26 deletions

View File

@ -15,7 +15,6 @@ import java.util.Set;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType; import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaOrder; 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.JpaQueryStructure;
import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmNode; 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.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; 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.Expression;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.metamodel.SingularAttribute;
/** /**
* Defines the commonality between a root query and a subquery. * Defines the commonality between a root query and a subquery.
@ -519,6 +521,10 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
collectSelectedFromSet( selectedFromSet, (SqmFrom<?, ?>) path.getLhs() ); 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<SqmFrom<?, ?>> selectedFromSet, SqmFrom<?, ?> sqmFrom) { private void collectSelectedFromSet(Set<SqmFrom<?, ?>> selectedFromSet, SqmFrom<?, ?> sqmFrom) {
@ -569,6 +575,22 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
} }
} }
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 @Override
public void appendHqlString(StringBuilder sb) { public void appendHqlString(StringBuilder sb) {
if ( selectClause != null ) { if ( selectClause != null ) {

View File

@ -6,10 +6,8 @@
*/ */
package org.hibernate.sql.results.graph.embeddable.internal; 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.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.query.SemanticException;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.FromClauseAccess;
@ -47,10 +45,6 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
this.fetchContainer = modelPart.getEmbeddableTypeDescriptor(); this.fetchContainer = modelPart.getEmbeddableTypeDescriptor();
this.resultVariable = resultVariable; 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. 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. 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<T> extends AbstractFetchParent implements Embe
return false; 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 @Override
public String getResultVariable() { public String getResultVariable() {
return resultVariable; return resultVariable;