From f1d6dc890a20dda445b51822868c9bfdb1e7fb01 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 24 Jun 2021 16:34:18 +0200 Subject: [PATCH] Fix Could not locate TableGroup exception when a join predicate contain an implicit join --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 6 +- .../orm/test/join/OuterJoinTest.java | 447 ++++++++++++++++++ .../query/hql/size/ManyToManySizeTest2.java | 2 +- .../query/hql/size/OneToManySizeTest2.java | 8 +- .../hibernate/test/join/OuterJoinTest.java | 399 ---------------- 5 files changed, 456 insertions(+), 406 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/join/OuterJoinTest.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index e1879fc501..b3c29e8b16 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -1930,8 +1930,8 @@ public abstract class BaseSqmToSqlAstConverter extends Base fromClauseIndex.register( sqmRoot, tableGroup ); currentQuerySpec().getFromClause().addRoot( tableGroup ); - consumeExplicitJoins( sqmRoot, tableGroup ); consumeReusablePaths( sqmRoot, tableGroup ); + consumeExplicitJoins( sqmRoot, tableGroup ); } private EntityPersister resolveEntityPersister(EntityDomainType entityDomainType) { @@ -2024,6 +2024,8 @@ public abstract class BaseSqmToSqlAstConverter extends Base getFromClauseIndex().register( sqmJoin, joinedTableGroup, joinPath ); + consumeReusablePaths( sqmJoin, joinedTableGroup ); + // add any additional join restrictions if ( sqmJoin.getJoinPredicate() != null ) { if ( sqmJoin.isFetched() ) { @@ -2040,7 +2042,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base } consumeExplicitJoins( sqmJoin, joinedTableGroup ); - consumeReusablePaths( sqmJoin, joinedTableGroup ); + } private NavigablePath getJoinNavigablePath( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/join/OuterJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/join/OuterJoinTest.java new file mode 100644 index 0000000000..a4bf1d9d0c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/join/OuterJoinTest.java @@ -0,0 +1,447 @@ +package org.hibernate.test.join; + +import java.util.List; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; +import javax.persistence.OneToOne; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.Table; +import javax.persistence.Tuple; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Jpa( + annotatedClasses = { + OuterJoinTest.A.class, + OuterJoinTest.B.class, + OuterJoinTest.C.class, + OuterJoinTest.D.class, + OuterJoinTest.Association.class + } +) +public class OuterJoinTest { + + @BeforeEach + public void setUp(EntityManagerFactoryScope scope) throws Exception { + scope.inTransaction( em -> { + Association association = new Association( 1l, "association" ); + em.merge( association ); + + em.merge( new A( 1L, "a", association ) ); + em.merge( new A( 2L, "b", association ) ); + em.merge( new A( 3L, "c", association ) ); + + em.merge( new B( 1L, "d", association ) ); + em.merge( new B( 2L, "e", association ) ); + em.merge( new B( 3L, "f", association ) ); + + em.merge( new C( 1L, "g", association ) ); + em.merge( new C( 2L, "h", association ) ); + em.merge( new C( 4L, "j", association ) ); + + em.merge( new D( 1L, "k", association ) ); + em.merge( new D( 2L, "l", association ) ); + em.merge( new D( 4L, "m", association ) ); + } ); + } + + @AfterAll + public void tearDown(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + entityManager.createQuery( "delete from A" ).executeUpdate(); + entityManager.createQuery( "delete from B" ).executeUpdate(); + entityManager.createQuery( "delete from C" ).executeUpdate(); + entityManager.createQuery( "delete from D" ).executeUpdate(); + entityManager.createQuery( "delete from Association" ).executeUpdate(); + } + ); + } + + @Test + public void mergeIt(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + Association association = new Association( 1l, "association" ); + em.merge( association ); + + em.merge( new A( 1L, "a", association ) ); + em.merge( new A( 2L, "b", association ) ); + em.merge( new A( 3L, "c", association ) ); + + em.merge( new B( 1L, "d", association ) ); + em.merge( new B( 2L, "e", association ) ); + em.merge( new B( 3L, "f", association ) ); + + em.merge( new C( 1L, "g", association ) ); + em.merge( new C( 2L, "h", association ) ); + em.merge( new C( 4L, "j", association ) ); + + em.merge( new D( 1L, "k", association ) ); + em.merge( new D( 2L, "l", association ) ); + em.merge( new D( 4L, "m", association ) ); + } ); + } + + @Test + public void testJoinOrderWithRightJoin(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key " + + "RIGHT JOIN C c ON a.key = c.key " + + "INNER JOIN D d ON d.key = c.key " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ) + .getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + public void testJoinOrderWithRightNormalJoin(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key " + + "RIGHT JOIN a.cAssociationByKey c " + + "INNER JOIN D d ON d.key = c.key " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ) + .getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + public void testJoinOrderWithRightJoinWithIdDereference(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key AND a.association.key = b.association.key " + + "RIGHT JOIN C c ON a.key = c.key AND a.association.key = c.association.key " + + "INNER JOIN D d ON d.key = c.key AND d.association.key = c.association.key " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ).getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + public void testJoinOrderWithRightNormalJoinWithIdDereference(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key AND a.association.key = b.association.key " + + "RIGHT JOIN a.cAssociationByKey c ON a.key = c.key AND a.association.key = c.association.key " + + "INNER JOIN D d ON d.key = c.key AND d.association.key = c.association.key " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ).getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + public void testJoinOrderWithRightJoinWithInnerImplicitJoins(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key,b.key,c.key,d.key) AS key, a.value AS aValue, b.value AS bValue, c.value AS cValue, d.value AS dValue " + + "FROM A a JOIN a.association association_1 JOIN B b ON (EXISTS (SELECT 1 FROM b.association _synth_subquery_0 WHERE a.key = b.key AND association_1.value = _synth_subquery_0.value))" + + "RIGHT JOIN C c ON (EXISTS (SELECT 1 FROM c.association _synth_subquery_0 WHERE a.key = c.key AND association_1.value = _synth_subquery_0.value)) " + + "JOIN c.association association_5 " + + "JOIN D d ON (EXISTS (SELECT 1 FROM d.association _synth_subquery_0 WHERE d.key = c.key AND _synth_subquery_0.value = association_5.value))" + + " ORDER BY COALESCE(a.key,b.key,c.key,d.key) ASC", + Tuple.class + ).getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + @Disabled("Hibernate doesn't support implicit joins") + public void testJoinOrderWithRightNormalJoinWithInnerImplicitJoins(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key AND a.association.value = b.association.value " + + "RIGHT JOIN a.cAssociationByKey c ON a.key = c.key AND a.association.value = c.association.value " + + "INNER JOIN D d ON d.key = c.key AND d.association.value = c.association.value " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ).getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + public void testJoinOrderWithRightJoinWithNonOptionalAssociationProjections(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key " + + "RIGHT JOIN C c ON a.key = c.key " + + "INNER JOIN D d ON d.key = c.key " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ).getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @Test + public void testJoinOrderWithRightNormalJoinWithNonOptionalAssociationProjections(EntityManagerFactoryScope scope) { + scope.inTransaction( em -> { + List resultList = em.createQuery( + "SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + + "FROM A a " + + "INNER JOIN B b ON a.key = b.key " + + "RIGHT JOIN a.cAssociationByKey c ON a.key = c.key " + + "INNER JOIN D d ON d.key = c.key " + + "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", + Tuple.class + ).getResultList(); + + assertEquals( 3, resultList.size() ); + + assertEquals( "a", resultList.get( 0 ).get( 1 ) ); + assertEquals( "d", resultList.get( 0 ).get( 2 ) ); + assertEquals( "g", resultList.get( 0 ).get( 3 ) ); + assertEquals( "k", resultList.get( 0 ).get( 4 ) ); + + assertEquals( "b", resultList.get( 1 ).get( 1 ) ); + assertEquals( "e", resultList.get( 1 ).get( 2 ) ); + assertEquals( "h", resultList.get( 1 ).get( 3 ) ); + assertEquals( "l", resultList.get( 1 ).get( 4 ) ); + + assertNull( resultList.get( 2 ).get( 1 ) ); + assertNull( resultList.get( 2 ).get( 2 ) ); + assertEquals( "j", resultList.get( 2 ).get( 3 ) ); + assertEquals( "m", resultList.get( 2 ).get( 4 ) ); + } ); + } + + @MappedSuperclass + public static class BaseEntity { + + @Id + @Column(name = "key_id") + Long key; + + String value; + + @ManyToOne(optional = false) + Association association; + + public BaseEntity() { + } + + public BaseEntity(Long key, String value, Association association) { + this.key = key; + this.value = value; + this.association = association; + } + } + + @Entity(name = "A") + @Table(name = "a") + public static class A extends BaseEntity { + + @OneToOne + @PrimaryKeyJoinColumn(columnDefinition = "association_key", referencedColumnName = "key_id") + private C cAssociationByKey; + + public A() { + } + + public A(Long key, String value, Association association) { + super( key, value, association ); + } + } + + + @Entity(name = "B") + @Table(name = "b") + public static class B extends BaseEntity { + public B() { + } + + public B(Long key, String value, Association association) { + super( key, value, association ); + } + } + + @Entity(name = "C") + @Table(name = "c") + public static class C extends BaseEntity { + public C() { + } + + public C(Long key, String value, Association association) { + super( key, value, association ); + } + } + + @Entity(name = "D") + @Table(name = "d") + public static class D extends BaseEntity { + public D() { + } + + public D(Long key, String value, Association association) { + super( key, value, association ); + } + + } + + @Entity(name = "Association") + @Table(name = "association") + public static class Association { + + @Id + @Column(name = "key_id") + private Long key; + + private String value; + + public Association() { + } + + public Association(Long key, String value) { + this.key = key; + this.value = value; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/ManyToManySizeTest2.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/ManyToManySizeTest2.java index 53e1b05cd4..36c885db61 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/ManyToManySizeTest2.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/ManyToManySizeTest2.java @@ -258,7 +258,7 @@ public class ManyToManySizeTest2 { Student.class ).getResultList(); assertEquals( 0, students.size() ); - statementInspector.assertNumberOfJoins( 0, 4 ); + statementInspector.assertNumberOfJoins( 0, 3 ); students = session.createQuery( "select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 1", diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/OneToManySizeTest2.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/OneToManySizeTest2.java index 446a1083fe..fa6b9339ae 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/OneToManySizeTest2.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/size/OneToManySizeTest2.java @@ -259,7 +259,7 @@ public class OneToManySizeTest2 { "where size(student.teacher.students) > 2", Student.class ).getResultList(); - assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); + assertEquals( 2, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); assertEquals( 0, students.size() ); students = session.createQuery( @@ -436,7 +436,7 @@ public class OneToManySizeTest2 { Student.class ).getResultList(); // Since the join for "student.teacher" is never used and is a non-optional association we don't generate a SQL join for it - assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); + assertEquals( 2, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); assertEquals( 3L, students.size() ); assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) ); assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) ); @@ -452,7 +452,7 @@ public class OneToManySizeTest2 { Student.class ).getResultList(); // Since the join for "student.teacher" is never used and is a non-optional association we don't generate a SQL join for it - assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); + assertEquals( 2, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); assertEquals( 3L, students.size() ); assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) ); assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) ); @@ -468,7 +468,7 @@ public class OneToManySizeTest2 { Student.class ).getResultList(); // Since the join for "student.teacher" is never used and is a non-optional association we don't generate a SQL join for it - assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); + assertEquals( 2, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) ); assertEquals( 2L, students.size() ); assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) ); assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java deleted file mode 100644 index b8cfd953dd..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/join/OuterJoinTest.java +++ /dev/null @@ -1,399 +0,0 @@ -package org.hibernate.test.join; - -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import javax.persistence.*; - -import java.util.List; - -import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class OuterJoinTest extends BaseCoreFunctionalTestCase { - - @Override - protected Class[] getAnnotatedClasses() { - return new Class[]{ A.class, B.class, C.class, D.class, Association.class }; - } - - - @MappedSuperclass - public static class BaseEntity { - - @Id - @Column(name = "key_id") - Long key; - - String value; - - @ManyToOne(optional = false) - Association association; - - public BaseEntity() { - } - - public BaseEntity(Long key, String value, Association association) { - this.key = key; - this.value = value; - this.association = association; - } - } - - @Entity(name = "A") - @Table(name = "a") - public static class A extends BaseEntity { - - @OneToOne - @PrimaryKeyJoinColumn(columnDefinition = "association_key", referencedColumnName = "key_id") - private C cAssociationByKey; - - public A() { - } - - public A(Long key, String value, Association association) { - super(key, value, association); - } - } - - - @Entity(name = "B") - @Table(name = "b") - public static class B extends BaseEntity { - public B() { - } - - public B(Long key, String value, Association association) { - super(key, value, association); - } - } - - @Entity(name = "C") - @Table(name = "c") - public static class C extends BaseEntity { - public C() { - } - - public C(Long key, String value, Association association) { - super(key, value, association); - } - } - - @Entity(name = "D") - @Table(name = "d") - public static class D extends BaseEntity { - public D() { - } - - public D(Long key, String value, Association association) { - super(key, value, association); - } - - } - - @Entity(name = "Association") - @Table(name = "association") - public static class Association { - - @Id - @Column(name = "key_id") - private Long key; - - private String value; - - public Association() { - } - - public Association(Long key, String value) { - this.key = key; - this.value = value; - } - } - - @Before - public void setUp() throws Exception { - doInJPA( this::sessionFactory, em -> { - Association association = new Association(1l, "association"); - em.merge(association); - - em.merge(new A(1L, "a", association)); - em.merge(new A(2L, "b", association)); - em.merge(new A(3L, "c", association)); - - em.merge(new B(1L, "d", association)); - em.merge(new B(2L, "e", association)); - em.merge(new B(3L, "f", association)); - - em.merge(new C(1L, "g", association)); - em.merge(new C(2L, "h", association)); - em.merge(new C(4L, "j", association)); - - em.merge(new D(1L, "k", association)); - em.merge(new D(2L, "l", association)); - em.merge(new D(4L, "m", association)); - }); - } - - @Test - public void mergeIt(){ - doInJPA( this::sessionFactory, em -> { - Association association = new Association(1l, "association"); - em.merge(association); - - em.merge(new A(1L, "a", association)); - em.merge(new A(2L, "b", association)); - em.merge(new A(3L, "c", association)); - - em.merge(new B(1L, "d", association)); - em.merge(new B(2L, "e", association)); - em.merge(new B(3L, "f", association)); - - em.merge(new C(1L, "g", association)); - em.merge(new C(2L, "h", association)); - em.merge(new C(4L, "j", association)); - - em.merge(new D(1L, "k", association)); - em.merge(new D(2L, "l", association)); - em.merge(new D(4L, "m", association)); - }); - } - - @Test - public void testJoinOrderWithRightJoin() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key " + - "RIGHT JOIN C c ON a.key = c.key " + - "INNER JOIN D d ON d.key = c.key " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class) - .getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - public void testJoinOrderWithRightNormalJoin() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key " + - "RIGHT JOIN a.cAssociationByKey c " + - "INNER JOIN D d ON d.key = c.key " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class) - .getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - public void testJoinOrderWithRightJoinWithIdDereference() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key AND a.association.key = b.association.key " + - "RIGHT JOIN C c ON a.key = c.key AND a.association.key = c.association.key " + - "INNER JOIN D d ON d.key = c.key AND d.association.key = c.association.key " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - public void testJoinOrderWithRightNormalJoinWithIdDereference() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key AND a.association.key = b.association.key " + - "RIGHT JOIN a.cAssociationByKey c ON a.key = c.key AND a.association.key = c.association.key " + - "INNER JOIN D d ON d.key = c.key AND d.association.key = c.association.key " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - public void testJoinOrderWithRightJoinWithInnerImplicitJoins() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key,b.key,c.key,d.key) AS key, a.value AS aValue, b.value AS bValue, c.value AS cValue, d.value AS dValue " + - "FROM A a JOIN a.association association_1 JOIN B b ON (EXISTS (SELECT 1 FROM b.association _synth_subquery_0 WHERE a.key = b.key AND association_1.value = _synth_subquery_0.value))" + - "RIGHT JOIN C c ON (EXISTS (SELECT 1 FROM c.association _synth_subquery_0 WHERE a.key = c.key AND association_1.value = _synth_subquery_0.value)) " + - "JOIN c.association association_5 " + - "JOIN D d ON (EXISTS (SELECT 1 FROM d.association _synth_subquery_0 WHERE d.key = c.key AND _synth_subquery_0.value = association_5.value))" + - " ORDER BY COALESCE(a.key,b.key,c.key,d.key) ASC", Tuple.class).getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - @Ignore("Hibernate doesn't support implicit joins") - public void testJoinOrderWithRightNormalJoinWithInnerImplicitJoins() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key AND a.association.value = b.association.value " + - "RIGHT JOIN a.cAssociationByKey c ON a.key = c.key AND a.association.value = c.association.value " + - "INNER JOIN D d ON d.key = c.key AND d.association.value = c.association.value " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - public void testJoinOrderWithRightJoinWithNonOptionalAssociationProjections() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key " + - "RIGHT JOIN C c ON a.key = c.key " + - "INNER JOIN D d ON d.key = c.key " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - - @Test - public void testJoinOrderWithRightNormalJoinWithNonOptionalAssociationProjections() { - doInJPA( this::sessionFactory, em -> { - List resultList = em.createQuery("SELECT COALESCE(a.key, b.key, c.key, d.key), a.value, b.value, c.value, d.value " + - "FROM A a " + - "INNER JOIN B b ON a.key = b.key " + - "RIGHT JOIN a.cAssociationByKey c ON a.key = c.key " + - "INNER JOIN D d ON d.key = c.key " + - "ORDER BY COALESCE(a.key, b.key, c.key, d.key) ASC", Tuple.class).getResultList(); - - assertEquals(3, resultList.size()); - - assertEquals("a", resultList.get(0).get(1)); - assertEquals("d", resultList.get(0).get(2)); - assertEquals("g", resultList.get(0).get(3)); - assertEquals("k", resultList.get(0).get(4)); - - assertEquals("b", resultList.get(1).get(1)); - assertEquals("e", resultList.get(1).get(2)); - assertEquals("h", resultList.get(1).get(3)); - assertEquals("l", resultList.get(1).get(4)); - - assertNull(resultList.get(2).get(1)); - assertNull(resultList.get(2).get(2)); - assertEquals("j", resultList.get(2).get(3)); - assertEquals("m", resultList.get(2).get(4)); - }); - } - -}