From 6bb8d03595cdca70bc82cdcc7df9096b345e276a Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 22 Jun 2015 17:56:18 -0700 Subject: [PATCH] HHH-9637 : Join is reused when 2 explicit joins are used for the same ToOne association --- .../hql/internal/ast/tree/DotNode.java | 12 +- .../test/hql/fetchAndJoin/Child.java | 80 +++++++ .../test/hql/fetchAndJoin/Entity1.java | 69 ++++++ .../test/hql/fetchAndJoin/Entity2.java | 70 ++++++ .../test/hql/fetchAndJoin/Entity3.java | 56 +++++ .../test/hql/fetchAndJoin/GrandChild.java | 65 ++++++ .../test/hql/fetchAndJoin/Parent.java | 80 +++++++ .../fetchAndJoin/ToManyFetchAndJoinTest.java | 203 ++++++++++++++++++ .../fetchAndJoin/ToOneFetchAndJoinTest.java | 159 ++++++++++++++ .../test/criteria/paths/FetchAndJoinTest.java | 69 ++++++ .../AbstractMetamodelSpecificTest.java | 1 + .../hibernate/jpa/test/metamodel/Entity1.java | 43 ++++ .../hibernate/jpa/test/metamodel/Entity2.java | 43 ++++ .../hibernate/jpa/test/metamodel/Entity3.java | 37 ++++ 14 files changed, 984 insertions(+), 3 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Child.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity1.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity2.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity3.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/GrandChild.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Parent.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToManyFetchAndJoinTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToOneFetchAndJoinTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/paths/FetchAndJoinTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity1.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity2.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity3.java diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java index ec2c7795e1..e72c67bb42 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java @@ -468,7 +468,7 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec boolean found = elem != null; // even though we might find a pre-existing element by join path, we may not be able to reuse it... - boolean useFoundFromElement = found && canReuse( elem ); + boolean useFoundFromElement = found && canReuse( classAlias, elem ); if ( !useFoundFromElement ) { // If this is an implied join in a from element, then use the impled join type which is part of the @@ -517,9 +517,10 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec setFromElement( elem ); // This 'dot' expression now refers to the resulting from element. } - private boolean canReuse(FromElement fromElement) { + private boolean canReuse(String classAlias, FromElement fromElement) { // if the from-clauses are the same, we can be a little more aggressive in terms of what we reuse - if ( fromElement.getFromClause() == getWalker().getCurrentFromClause() ) { + if ( fromElement.getFromClause() == getWalker().getCurrentFromClause() && + areSame( classAlias, fromElement.getClassAlias() )) { return true; } @@ -527,6 +528,11 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec return getWalker().getCurrentClauseType() != SqlTokenTypes.FROM; } + private boolean areSame(String alias1, String alias2) { + // again, null != null here + return !StringHelper.isEmpty( alias1 ) && !StringHelper.isEmpty( alias2 ) && alias1.equals( alias2 ); + } + private void setImpliedJoin(FromElement elem) { this.impliedJoin = elem; if ( getFirstChild().getType() == SqlTokenTypes.DOT ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Child.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Child.java new file mode 100644 index 0000000000..74667248b8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Child.java @@ -0,0 +1,80 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name = "entity1") +public class Child { + @Id + @GeneratedValue + private long id; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn + private Set grandChildren = new HashSet(); + + public Child() { + } + + public Child(String value) { + this.value = value; + } + + private String value; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Set getGrandChildren() { + return grandChildren; + } + + public void setGrandChildren(Set grandChildren) { + this.grandChildren = grandChildren; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity1.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity1.java new file mode 100644 index 0000000000..2e9778484b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity1.java @@ -0,0 +1,69 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "entity1") +public class Entity1 { + @Id + @GeneratedValue + private long id; + + @ManyToOne + @JoinColumn(name="entity2_id", nullable = false) + private Entity2 entity2; + + private String value; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Entity2 getEntity2() { + return entity2; + } + + public void setEntity2(Entity2 entity2) { + this.entity2 = entity2; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity2.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity2.java new file mode 100644 index 0000000000..3edf588156 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity2.java @@ -0,0 +1,70 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "entity2") +public class Entity2 { + @Id + @GeneratedValue + private long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="entity3_id") + private Entity3 entity3; + + private String value; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Entity3 getEntity3() { + return entity3; + } + + public void setEntity3(Entity3 entity3) { + this.entity3 = entity3; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity3.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity3.java new file mode 100644 index 0000000000..54234438a1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Entity3.java @@ -0,0 +1,56 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "entity3") +public class Entity3 { + @Id + @GeneratedValue + private long id; + + private String value; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/GrandChild.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/GrandChild.java new file mode 100644 index 0000000000..d135c7a7b9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/GrandChild.java @@ -0,0 +1,65 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name = "entity1") +public class GrandChild { + @Id + @GeneratedValue + private long id; + + private String value; + + public GrandChild() { + } + + public GrandChild(String value) { + this.value = value; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Parent.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Parent.java new file mode 100644 index 0000000000..5329694146 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/Parent.java @@ -0,0 +1,80 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name = "entity1") +public class Parent { + @Id + @GeneratedValue + private long id; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn + private Set children = new HashSet(); + + private String value; + + public Parent() { + } + + public Parent(String value) { + this.value = value; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children = children; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToManyFetchAndJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToManyFetchAndJoinTest.java new file mode 100644 index 0000000000..98541f5dd3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToManyFetchAndJoinTest.java @@ -0,0 +1,203 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import java.util.Iterator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Gail Badner + */ +public class ToManyFetchAndJoinTest extends BaseCoreFunctionalTestCase { + + @Before + public void setupData() { + Parent p = new Parent( "p" ); + Child c1 = new Child( "c1" ); + GrandChild gc11 = new GrandChild( "gc11" ); + GrandChild gc12 = new GrandChild( "gc12" ); + p.getChildren().add( c1 ); + c1.getGrandChildren().add( gc11 ); + c1.getGrandChildren().add( gc12 ); + + Child c2 = new Child( "c2" ); + GrandChild gc21 = new GrandChild( "gc21" ); + GrandChild gc22 = new GrandChild( "gc22" ); + GrandChild gc23 = new GrandChild( "gc23" ); + p.getChildren().add( c2 ); + c2.getGrandChildren().add( gc21 ); + c2.getGrandChildren().add( gc22 ); + c2.getGrandChildren().add( gc23 ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( p ); + s.getTransaction().commit(); + s.close(); + } + + @After + public void cleanupData() { + Session s = openSession(); + s.getTransaction().begin(); + s.createQuery( "delete Parent" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testExplicitJoinBeforeFetchJoins() { + + Session s = openSession(); + s.getTransaction().begin(); + + Parent p = + (Parent) s.createQuery( + "select p from Parent p inner join p.children cRestrict inner join fetch p.children c inner join fetch c.grandChildren where cRestrict.value = 'c1'" ) + .uniqueResult(); + + assertEquals( "p", p.getValue() ); + assertTrue( Hibernate.isInitialized( p.getChildren() ) ); + assertEquals( 2, p.getChildren().size() ); + Iterator iterator = p.getChildren().iterator(); + Child cA = iterator.next(); + assertTrue( Hibernate.isInitialized( cA.getGrandChildren() ) ); + if ( cA.getValue().equals( "c1" ) ) { + assertEquals( 2, cA.getGrandChildren().size() ); + Child cB = iterator.next(); + assertTrue( Hibernate.isInitialized( cB.getGrandChildren() ) ); + assertEquals( 3, cB.getGrandChildren().size() ); + } + else if ( cA.getValue().equals( "c2" ) ) { + assertEquals( 3, cA.getGrandChildren().size() ); + Child cB = iterator.next(); + assertTrue( Hibernate.isInitialized( cB.getGrandChildren() ) ); + assertEquals( 2, cB.getGrandChildren().size() ); + } + else { + fail( "unexpected value" ); + } + + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testExplicitJoinBetweenFetchJoins() { + + Session s = openSession(); + s.getTransaction().begin(); + + Parent p = + (Parent) s.createQuery( + "select p from Parent p inner join fetch p.children c inner join p.children cRestrict inner join fetch c.grandChildren where cRestrict.value = 'c1'" ) + .uniqueResult(); + + assertEquals( "p", p.getValue() ); + assertTrue( Hibernate.isInitialized( p.getChildren() ) ); + assertEquals( 2, p.getChildren().size() ); + Iterator iterator = p.getChildren().iterator(); + Child cA = iterator.next(); + assertTrue( Hibernate.isInitialized( cA.getGrandChildren() ) ); + if ( cA.getValue().equals( "c1" ) ) { + assertEquals( 2, cA.getGrandChildren().size() ); + Child cB = iterator.next(); + assertTrue( Hibernate.isInitialized( cB.getGrandChildren() ) ); + assertEquals( 3, cB.getGrandChildren().size() ); + } + else if ( cA.getValue().equals( "c2" ) ) { + assertEquals( 3, cA.getGrandChildren().size() ); + Child cB = iterator.next(); + assertTrue( Hibernate.isInitialized( cB.getGrandChildren() ) ); + assertEquals( 2, cB.getGrandChildren().size() ); + } + else { + fail( "unexpected value" ); + } + + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testExplicitJoinAfterFetchJoins() { + + Session s = openSession(); + s.getTransaction().begin(); + + Parent p = + (Parent) s.createQuery( + "select p from Parent p inner join fetch p.children c inner join fetch c.grandChildren inner join p.children cRestrict where cRestrict.value = 'c1'" ) + .uniqueResult(); + + assertEquals( "p", p.getValue() ); + assertTrue( Hibernate.isInitialized( p.getChildren() ) ); + assertEquals( 2, p.getChildren().size() ); + Iterator iterator = p.getChildren().iterator(); + Child cA = iterator.next(); + assertTrue( Hibernate.isInitialized( cA.getGrandChildren() ) ); + if ( cA.getValue().equals( "c1" ) ) { + assertEquals( 2, cA.getGrandChildren().size() ); + Child cB = iterator.next(); + assertTrue( Hibernate.isInitialized( cB.getGrandChildren() ) ); + assertEquals( 3, cB.getGrandChildren().size() ); + } + else if ( cA.getValue().equals( "c2" ) ) { + assertEquals( 3, cA.getGrandChildren().size() ); + Child cB = iterator.next(); + assertTrue( Hibernate.isInitialized( cB.getGrandChildren() ) ); + assertEquals( 2, cB.getGrandChildren().size() ); + } + else { + fail( "unexpected value" ); + } + + s.getTransaction().commit(); + s.close(); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ + Parent.class, + Child.class, + GrandChild.class + }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToOneFetchAndJoinTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToOneFetchAndJoinTest.java new file mode 100644 index 0000000000..4a6f7cbd59 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/fetchAndJoin/ToOneFetchAndJoinTest.java @@ -0,0 +1,159 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql.fetchAndJoin; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class ToOneFetchAndJoinTest extends BaseCoreFunctionalTestCase { + + @Before + public void setupData() { + Entity1 e1 = new Entity1(); + e1.setValue( "entity1" ); + Entity2 e2 = new Entity2(); + e2.setValue( "entity2" ); + Entity3 e3 = new Entity3(); + e3.setValue( "entity3" ); + + e1.setEntity2( e2 ); + e2.setEntity3( e3 ); + + Entity2 e2a = new Entity2(); + e2a.setValue( "entity2a" ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( e3 ); + s.persist( e2 ); + s.persist( e1 ); + s.persist( e2a ); + s.getTransaction().commit(); + s.close(); + } + + @After + public void cleanupData() { + Session s = openSession(); + s.getTransaction().begin(); + s.createQuery( "delete Entity1" ).executeUpdate(); + s.createQuery( "delete Entity2" ).executeUpdate(); + s.createQuery( "delete Entity3" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testFetchJoinsWithImplicitJoinInRestriction() { + + Session s = openSession(); + s.getTransaction().begin(); + + Entity1 e1Queryied = + (Entity1) s.createQuery( + "select e1 from Entity1 e1 inner join fetch e1.entity2 e2 inner join fetch e2.entity3 where e1.entity2.value = 'entity2'" ) + .uniqueResult(); + assertEquals( "entity1", e1Queryied.getValue() ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2() ) ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2().getEntity3() ) ); + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testExplicitJoinBeforeFetchJoins() { + + Session s = openSession(); + s.getTransaction().begin(); + + Entity1 e1Queryied = + (Entity1) s.createQuery( + "select e1 from Entity1 e1 inner join e1.entity2 e1Restrict inner join fetch e1.entity2 e2 inner join fetch e2.entity3 where e1Restrict.value = 'entity2'" ) + .uniqueResult(); + assertEquals( "entity1", e1Queryied.getValue() ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2() ) ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2().getEntity3() ) ); + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testExplicitJoinBetweenFetchJoins() { + + Session s = openSession(); + s.getTransaction().begin(); + + Entity1 e1Queryied = + (Entity1) s.createQuery( + "select e1 from Entity1 e1 inner join fetch e1.entity2 e2 inner join e1.entity2 e1Restrict inner join fetch e2.entity3 where e1Restrict.value = 'entity2'" ) + .uniqueResult(); + assertEquals( "entity1", e1Queryied.getValue() ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2() ) ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2().getEntity3() ) ); + s.getTransaction().commit(); + s.close(); + } + + @Test + @TestForIssue( jiraKey = "HHH-9637") + public void testExplicitJoinAfterFetchJoins() { + + Session s = openSession(); + s.getTransaction().begin(); + + Entity1 e1Queryied = + (Entity1) s.createQuery( + "select e1 from Entity1 e1 inner join fetch e1.entity2 e2 inner join fetch e2.entity3 inner join e1.entity2 e1Restrict where e1Restrict.value = 'entity2'" ) + .uniqueResult(); + assertEquals( "entity1", e1Queryied.getValue() ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2() ) ); + assertTrue( Hibernate.isInitialized( e1Queryied.getEntity2().getEntity3() ) ); + s.getTransaction().commit(); + s.close(); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ + Entity1.class, + Entity2.class, + Entity3.class + }; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/paths/FetchAndJoinTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/paths/FetchAndJoinTest.java new file mode 100644 index 0000000000..a71f330e65 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/paths/FetchAndJoinTest.java @@ -0,0 +1,69 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.criteria.paths; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Fetch; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Root; + +import org.junit.Test; + +import org.hibernate.jpa.test.metamodel.AbstractMetamodelSpecificTest; +import org.hibernate.jpa.test.metamodel.Entity1; +import org.hibernate.jpa.test.metamodel.Entity1_; +import org.hibernate.jpa.test.metamodel.Entity2; +import org.hibernate.jpa.test.metamodel.Entity2_; + +/** + * @author Gail Badner + */ +public class FetchAndJoinTest extends AbstractMetamodelSpecificTest { + + @Test + public void testImplicitJoinFromExplicitCollectionJoin() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + final CriteriaBuilder builder = em.getCriteriaBuilder(); + final CriteriaQuery criteria = builder.createQuery(Entity1.class); + + final Root root = criteria.from(Entity1.class); + final Join entity2Join = root.join( Entity1_.entity2, JoinType.INNER); // illegal with fetch join + + final Fetch entity2Fetch = root.fetch(Entity1_.entity2, JoinType.INNER); // <=== REMOVE + entity2Fetch.fetch( Entity2_.entity3 ); // <=== REMOVE + + criteria.where(builder.equal(root.get(Entity1_.value), "test"), + builder.equal(entity2Join.get(Entity2_.value), "test")); // illegal with fetch join + + em.createQuery(criteria).getResultList(); + + em.getTransaction().commit(); + em.close(); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/AbstractMetamodelSpecificTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/AbstractMetamodelSpecificTest.java index 2f28caf784..dc76b227c7 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/AbstractMetamodelSpecificTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/AbstractMetamodelSpecificTest.java @@ -15,6 +15,7 @@ public abstract class AbstractMetamodelSpecificTest extends BaseEntityManagerFun public Class[] getAnnotatedClasses() { return new Class[] { Address.class, Alias.class, Country.class, CreditCard.class, Customer.class, + Entity1.class, Entity2.class, Entity3.class, Info.class, LineItem.class, Order.class, Phone.class, Product.class, ShelfLife.class, Spouse.class, Thing.class, ThingWithQuantity.class, VersionedEntity.class diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity1.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity1.java new file mode 100644 index 0000000000..09d6be664b --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity1.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.metamodel; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "entity1") +public class Entity1 { + @Id + private long id; + + @ManyToOne + @JoinColumn(name="entity2_id", nullable = false) + private Entity2 entity2; + + private String value; +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity2.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity2.java new file mode 100644 index 0000000000..362ccc11eb --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity2.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.metamodel; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "entity2") +public class Entity2 { + @Id + private long id; + + @ManyToOne + @JoinColumn(name="entity3_id") + private Entity3 entity3; + + private String value; +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity3.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity3.java new file mode 100644 index 0000000000..af804816b6 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/metamodel/Entity3.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2015, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.metamodel; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "entity3") +public class Entity3 { + @Id + private long id; + + private String value; +}