From b8a3baa5c1c063ecf20c9c77b143b8278aec41a9 Mon Sep 17 00:00:00 2001 From: Giovanni Lovato Date: Tue, 23 Aug 2016 21:14:03 +0200 Subject: [PATCH] HHH-10485 : Lazy collections fetched with EntityGraph load with JOIN --- .../EntityGraphWithFetchAnnotationTest.java | 217 ++++++++++++++++++ ...reparedStatementSpyConnectionProvider.java | 11 + 2 files changed, 228 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphWithFetchAnnotationTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphWithFetchAnnotationTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphWithFetchAnnotationTest.java new file mode 100644 index 0000000000..13437da051 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/graphs/EntityGraphWithFetchAnnotationTest.java @@ -0,0 +1,217 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.jpa.test.graphs; + +import java.util.List; +import java.util.Map; +import javax.persistence.Entity; +import javax.persistence.EntityGraph; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; + +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.jpa.QueryHints; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.test.util.jdbc.PreparedStatementSpyConnectionProvider; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Vlad Mihalcea + */ +public class EntityGraphWithFetchAnnotationTest + extends BaseEntityManagerFunctionalTestCase { + + private PreparedStatementSpyConnectionProvider connectionProvider; + + @Override + protected Map getConfig() { + Map config = super.getConfig(); + config.put( + org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER, + connectionProvider + ); + return config; + } + + @Override + public void buildEntityManagerFactory() throws Exception { + connectionProvider = new PreparedStatementSpyConnectionProvider(); + super.buildEntityManagerFactory(); + } + + @Override + public void releaseResources() { + super.releaseResources(); + connectionProvider.stop(); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Order.class, + Product.class, + Tag.class, + }; + } + + @Test + @TestForIssue(jiraKey = "HHH-10485") + public void testWithoutEntityGraph() { + + doInJPA( this::entityManagerFactory, entityManager -> { + CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder + .createQuery( Order.class ); + criteriaQuery.from( Order.class ); + + connectionProvider.clear(); + + entityManager + .createQuery( criteriaQuery ) + .setFirstResult( 10 ) + .setMaxResults( 20 ) + .getResultList(); + + assertFalse( connectionProvider.getPreparedSQLStatements().get( 0 ).toLowerCase().contains( "left outer join" ) ); + } ); + } + + @Test + @TestForIssue(jiraKey = "HHH-10485") + public void testWithEntityGraph() { + + doInJPA( this::entityManagerFactory, entityManager -> { + CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder + .createQuery( Order.class ); + criteriaQuery.from( Order.class ); + + EntityGraph entityGraph = entityManager.createEntityGraph( Order.class ); + entityGraph.addAttributeNodes( "products" ); + + connectionProvider.clear(); + + entityManager + .createQuery( criteriaQuery ) + .setFirstResult( 10 ) + .setMaxResults( 20 ) + .setHint( QueryHints.HINT_FETCHGRAPH, entityGraph ) + .getResultList(); + + assertTrue( connectionProvider.getPreparedSQLStatements().get( 0 ).toLowerCase().contains( "left outer join" ) ); + } ); + } + + @Entity(name = "Order") + @Table(name = "orders") + public static class Order { + + @Id + @GeneratedValue + private long id; + + @OneToMany + @Fetch(FetchMode.SELECT) + private List products; + + @OneToMany + @Fetch(FetchMode.SELECT) + private List tags; + + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } + + public List getProducts() { + return this.products; + } + + public void setProducts(List products) { + this.products = products; + } + + public List getTags() { + return this.tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + } + + @Entity(name = "Product") + @Table(name = "products") + public static class Product { + + @Id + @GeneratedValue + private long id; + + private String name; + + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + } + + @Entity(name = "Tag") + @Table(name = "tags") + public static class Tag { + + @Id + @GeneratedValue + private long id; + + private String name; + + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java index 9a2551af27..ac3c417a6b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java @@ -116,6 +116,8 @@ public class PreparedStatementSpyConnectionProvider releasedConnections.clear(); preparedStatementMap.keySet().forEach( Mockito::reset ); preparedStatementMap.clear(); + executeStatements.clear(); + executeUpdateStatements.clear(); } /** @@ -164,6 +166,15 @@ public class PreparedStatementSpyConnectionProvider return new ArrayList<>( preparedStatementMap.keySet() ); } + /** + * Get the PreparedStatements SQL statements. + * + * @return list of recorded PreparedStatements SQL statements. + */ + public List getPreparedSQLStatements() { + return new ArrayList<>( preparedStatementMap.values() ); + } + /** * Get the SQL statements that were executed since the last clear operation. * @return list of recorded update statements.