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 01d340f101..c91b5d308f 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 @@ -298,6 +298,17 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec String propName = getPath(); FromClause currentFromClause = getWalker().getCurrentFromClause(); + // If the lhs of the join is a "component join", we need to go back to the + // first non-component-join as the origin to properly link aliases and + // join columns + FromElement lhsFromElement = getLhs().getFromElement(); + while ( lhsFromElement != null && ComponentJoin.class.isInstance( lhsFromElement ) ) { + lhsFromElement = lhsFromElement.getOrigin(); + } + if ( lhsFromElement == null ) { + throw new QueryException( "Unable to locate appropriate lhs" ); + } + // determine whether we should use the table name or table alias to qualify the column names... // we need to use the table-name when: // 1) the top-level statement is not a SELECT @@ -307,7 +318,6 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec // the alias also, even if the FromElement is the root one... // // in all other cases, we should use the table alias - final FromElement lhsFromElement = getLhs().getFromElement(); if ( getWalker().getStatementType() != SqlTokenTypes.SELECT ) { if ( isFromElementUpdateOrDeleteRoot( lhsFromElement ) ) { // at this point we know we have the 2 conditions above, @@ -330,7 +340,7 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec // it makes sense to join twice on the same collection role FromElementFactory factory = new FromElementFactory( currentFromClause, - getLhs().getFromElement(), + lhsFromElement, propName, classAlias, getColumns(), diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java index 513c3730bd..610c094242 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/embedded/EmbeddedTest.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import org.hibernate.Hibernate; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; @@ -43,6 +44,7 @@ import org.hibernate.test.util.SchemaUtil; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -407,6 +409,64 @@ public class EmbeddedTest extends BaseNonConfigCoreFunctionalTestCase { s.close(); } + @Test + @TestForIssue( jiraKey = "HHH-9642") + public void testEmbeddedAndOneToManyHql() throws Exception { + Session s; + s = openSession(); + Transaction tx = s.beginTransaction(); + InternetProvider provider = new InternetProvider(); + provider.setBrandName( "Fido" ); + LegalStructure structure = new LegalStructure(); + structure.setCountry( "Canada" ); + structure.setName( "Rogers" ); + provider.setOwner( structure ); + s.persist( provider ); + Manager manager = new Manager(); + manager.setName( "Bill" ); + manager.setEmployer( provider ); + structure.getTopManagement().add( manager ); + s.persist( manager ); + tx.commit(); + s.close(); + + s = openSession(); + s.getTransaction().begin(); + InternetProvider internetProviderQueried = + (InternetProvider) s.createQuery( "from InternetProvider" ).uniqueResult(); + assertFalse( Hibernate.isInitialized( internetProviderQueried.getOwner().getTopManagement() ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.getTransaction().begin(); + internetProviderQueried = + (InternetProvider) s.createQuery( "from InternetProvider i join fetch i.owner.topManagement" ) + .uniqueResult(); + assertTrue( Hibernate.isInitialized( internetProviderQueried.getOwner().getTopManagement() ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.getTransaction().begin(); + internetProviderQueried = + (InternetProvider) s.createQuery( "from InternetProvider i join fetch i.owner o join fetch o.topManagement" ) + .uniqueResult(); + assertTrue( Hibernate.isInitialized( internetProviderQueried.getOwner().getTopManagement() ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + tx = s.beginTransaction(); + provider = (InternetProvider) s.get( InternetProvider.class, provider.getId() ); + manager = provider.getOwner().getTopManagement().iterator().next(); + s.delete( manager ); + s.delete( provider ); + tx.commit(); + s.close(); + } + + @Test public void testDefaultCollectionTable() throws Exception { //are the tables correct? diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java index 106fc78f77..404fa01327 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java @@ -317,6 +317,75 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { s.close(); } + @Test + @TestForIssue( jiraKey = "HHH-9642") + public void testLazyAssociationInComponent() { + Session session = openSession(); + session.getTransaction().begin(); + + Address address = new Address(); + Zoo zoo = new Zoo( "ZOO 1", address ); + address.setCity( "City 1" ); + StateProvince stateProvince = new StateProvince(); + stateProvince.setName( "Illinois" ); + session.save( stateProvince ); + address.setStateProvince( stateProvince ); + session.save( zoo ); + + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.getTransaction().begin(); + + zoo = (Zoo) session.createQuery( "from Zoo z" ).uniqueResult(); + assertNotNull( zoo ); + assertNotNull( zoo.getAddress() ); + assertEquals( "City 1", zoo.getAddress().getCity() ); + assertFalse( Hibernate.isInitialized( zoo.getAddress().getStateProvince() ) ); + assertEquals( "Illinois", zoo.getAddress().getStateProvince().getName() ); + assertTrue( Hibernate.isInitialized( zoo.getAddress().getStateProvince() ) ); + + session.getTransaction().commit(); + session.close(); + + + session = openSession(); + session.getTransaction().begin(); + + zoo = (Zoo) session.createQuery( "from Zoo z join fetch z.address.stateProvince" ).uniqueResult(); + assertNotNull( zoo ); + assertNotNull( zoo.getAddress() ); + assertEquals( "City 1", zoo.getAddress().getCity() ); + assertTrue( Hibernate.isInitialized( zoo.getAddress().getStateProvince() ) ); + assertEquals( "Illinois", zoo.getAddress().getStateProvince().getName() ); + + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.getTransaction().begin(); + + zoo = (Zoo) session.createQuery( "from Zoo z join fetch z.address a join fetch a.stateProvince" ).uniqueResult(); + assertNotNull( zoo ); + assertNotNull( zoo.getAddress() ); + assertEquals( "City 1", zoo.getAddress().getCity() ); + assertTrue( Hibernate.isInitialized( zoo.getAddress().getStateProvince() ) ); + assertEquals( "Illinois", zoo.getAddress().getStateProvince().getName() ); + + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.getTransaction().begin(); + + zoo.getAddress().setStateProvince( null ); + session.delete( stateProvince ); + session.delete( zoo ); + session.getTransaction().commit(); + session.close(); + } + @Test public void testJPAQLQualifiedIdentificationVariablesControl() { // just checking syntax here... diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Alias.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Alias.java new file mode 100644 index 0000000000..327b5667f2 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Alias.java @@ -0,0 +1,77 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, 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.components; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +@Entity +public class Alias { + private Long id; + private Name name; + private String source; + + public Alias() { + } + + public Alias(String firstName, String lastName, String source) { + this( new Name( firstName, lastName ), source ); + } + + public Alias(Name name, String source) { + this.name = name; + this.source = source; + } + + @Id @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Name getName() { + return name; + } + + public void setName(Name name) { + this.name = name; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java index 791ce87ad7..32ddff5254 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java @@ -24,26 +24,33 @@ package org.hibernate.jpa.test.criteria.components; import java.util.List; +import java.util.Set; import javax.persistence.EntityManager; +import javax.persistence.Query; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Fetch; import javax.persistence.criteria.Root; import org.junit.Assert; import org.junit.Test; +import org.hibernate.Hibernate; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.TestForIssue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + /** * @author alan.oleary */ public class ComponentCriteriaTest extends BaseEntityManagerFunctionalTestCase { @Override public Class[] getAnnotatedClasses() { - return new Class[] { Client.class }; + return new Class[] { Client.class, Alias.class }; } @Test @@ -84,6 +91,66 @@ public class ComponentCriteriaTest extends BaseEntityManagerFunctionalTestCase { em.close(); } + @Test + @TestForIssue( jiraKey = "HHH-9642") + public void testOneToManyJoinFetchedInEmbeddable() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + Client client = new Client( 111, "steve", "ebersole" ); + Alias alias = new Alias( "a", "guy", "work" ); + client.getName().getAliases().add( alias ); + em.persist(client); + em.getTransaction().commit(); + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Client.class); + Root root = cq.from(Client.class); + root.fetch( Client_.name ).fetch( Name_.aliases ); + cq.where(cb.equal(root.get("name").get("firstName"), client.getName().getFirstName())); + List list = em.createQuery(cq).getResultList(); + Assert.assertEquals( 1, list.size() ); + client = list.get( 0 ); + assertTrue( Hibernate.isInitialized( client.getName().getAliases() ) ); + em.getTransaction().commit(); + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + TypedQuery< Client > q = em.createQuery( + "SELECT c FROM Client c JOIN FETCH c.name.aliases WHERE c.name.firstName = '" + + client.getName().getFirstName() + "'", + Client.class + ); + Assert.assertEquals( 1, q.getResultList().size() ); + client = list.get( 0 ); + assertTrue( Hibernate.isInitialized( client.getName().getAliases() ) ); + em.getTransaction().commit(); + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + q = em.createQuery( + "SELECT c FROM Client c JOIN c.name n join FETCH n.aliases WHERE c.name.firstName = '" + + client.getName().getFirstName() + "'", + Client.class + ); + Assert.assertEquals( 1, q.getResultList().size() ); + client = list.get( 0 ); + assertTrue( Hibernate.isInitialized( client.getName().getAliases() ) ); + em.getTransaction().commit(); + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + client = em.merge( client ); + em.remove( client ); + em.getTransaction().commit(); + em.close(); + } + @Test @TestForIssue( jiraKey = "HHH-4586" ) public void testParameterizedFunctions() { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Name.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Name.java index 7cfa826a9a..cbf143fe5b 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Name.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/Name.java @@ -21,8 +21,13 @@ */ package org.hibernate.jpa.test.criteria.components; import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Embeddable; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; /** * The name component @@ -35,6 +40,7 @@ public class Name implements Serializable { private String firstName; private String lastName; + private Set aliases = new HashSet( ); public Name() { } @@ -61,4 +67,13 @@ public class Name implements Serializable { public void setLastName(String lastName) { this.lastName = lastName; } + + @OneToMany(cascade = CascadeType.ALL) + public Set getAliases() { + return aliases; + } + + public void setAliases(Set aliases) { + this.aliases = aliases; + } } \ No newline at end of file