diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java index 4f24021cb9..f304ff7123 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/FromElementFactory.java @@ -368,7 +368,9 @@ public class FromElementFactory implements SqlTokenTypes { // origin.addDestination( destination ); // This was the cause of HHH-242 // origin.setType( FROM_FRAGMENT ); // Set the parent node type so that the AST is properly formed. - origin.setText( "" ); // The destination node will have all the FROM text. + if ( origin.getQueryableCollection() != null ) { + origin.setText( "" ); // The destination node will have all the FROM text. + } origin.setCollectionJoin( true ); // The parent node is a collection join too (voodoo - see JoinProcessor) fromClause.addCollectionJoinFromElementByPath( path, destination ); fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() ); diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java index 033ebbf1ab..93b8c17451 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SelectClause.java @@ -193,7 +193,7 @@ public class SelectClause extends SelectExpressionList { FromElement fromElement = (FromElement) iterator.next(); if ( fromElement.isFetch() ) { - FromElement origin = null; + FromElement origin; if ( fromElement.getRealOrigin() == null ) { // work around that crazy issue where the tree contains // "empty" FromElements (no text); afaict, this is caused diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java new file mode 100644 index 0000000000..80b6523de5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/FetchNonRootRelativeElementCollectionAndAssociationTest.java @@ -0,0 +1,71 @@ +package org.hibernate.test.hql; + +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import javax.persistence.*; + +import java.util.HashMap; +import java.util.Map; + +import static javax.persistence.CascadeType.ALL; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; + +/** + * @author Moritz Becker (moritz.becker@ordami.com) + * @date 12/01/2019 + * @company ordami GmbH + */ +@TestForIssue(jiraKey = "HHH-13201") +public class FetchNonRootRelativeElementCollectionAndAssociationTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { ProductNaturalId.class, Product.class, ProductDetail.class }; + } + + @Test + public void testJoinedSubclassUpdateWithCorrelation() { + doInJPA( this::entityManagerFactory, entityManager -> { + // DO NOT CHANGE this query: it used to trigger an error caused + // by the origin FromElement for the association fetch being resolved to the wrong FromElement due to the + // presence of an element collection join. + String u = "select prod from ProductNaturalId nat inner join nat.product prod " + + "left join fetch prod.productDetail " + + "left join fetch prod.normalizedPricesByUnit"; + Query query = entityManager.createQuery( u, Product.class ); + query.getResultList(); + } ); + } + + @Entity(name = "ProductNaturalId") + public class ProductNaturalId { + @Id + private String naturalId; + @OneToOne(optional = false) + private Product product; + } + + @Entity(name = "Product") + public class Product { + @Id + private Long id; + @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) + private ProductDetail productDetail; + @OneToOne(mappedBy = "product", cascade = ALL, fetch = FetchType.LAZY) + private ProductNaturalId naturalId; + @ElementCollection(fetch = FetchType.LAZY) + private Map normalizedPricesByUnit = new HashMap<>(); + } + + @Entity(name = "ProductDetail") + public class ProductDetail { + @Id + private Long id; + @OneToOne(optional = false) + @JoinColumn(name = "id") + @MapsId + private Product product; + } +}