HHH-17528 Throw error for embedded result with collections

This commit is contained in:
Marco Belladelli 2023-12-12 15:59:23 +01:00 committed by Christian Beikov
parent a59119ee42
commit b44ff032c3
2 changed files with 35 additions and 0 deletions

View File

@ -610,3 +610,11 @@ create table JsonHolder as (
==== ====
Again, the name and the nullability of the `aggregate` column can be refined through applying a `@Column` on the persistent attribute. Again, the name and the nullability of the `aggregate` column can be refined through applying a `@Column` on the persistent attribute.
[[embeddable-mapping-aggregate]]
==== Embeddable mappings containing collections
Mapping <<chapters/domain/collections.adoc#collections,collections>> inside an `@Embeddable` value is supported in most cases. There are a couple exceptions:
* If the values of an <<chapters/domain/collections.adoc#collections-elemental,@ElementCollection>> is of embeddable type, that embeddable cannot contain nested collections;
* Explicitly selecting an embeddable that contains collections in a query is currently not supported (we wouldn't be able to correctly initialize the collection since its owning entity instance would be missing from the Persistence Context).

View File

@ -6,8 +6,10 @@
*/ */
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;
@ -41,8 +43,14 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
super( navigablePath ); super( navigablePath );
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.
@ -88,6 +96,25 @@ 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;