HHH-8760 improve EntityGraph JPQL query hint test

This commit is contained in:
Brett Meyer 2013-12-02 12:43:45 -05:00
parent 685e33ae0c
commit e0d6cdc0f7
3 changed files with 41 additions and 13 deletions

View File

@ -119,14 +119,18 @@ public class QueryHints {
public static final String NATIVE_LOCKMODE = "org.hibernate.lockMode"; public static final String NATIVE_LOCKMODE = "org.hibernate.lockMode";
/** /**
* Hint providing an EntityGraph. With JPQL/HQL, the sole functionality is attribute nodes are treated as * Hint providing a "fetchgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER. Laziness is not affected. * FetchType.EAGER (via join fetch or subsequent select).
*
* Note: Currently, attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending
* on the attribute's definition in metadata, rather than forcing FetchType.LAZY.
*/ */
public static final String FETCHGRAPH = "javax.persistence.fetchgraph"; public static final String FETCHGRAPH = "javax.persistence.fetchgraph";
/** /**
* Hint providing an EntityGraph. With JPQL/HQL, the sole functionality is attribute nodes are treated as * Hint providing a "loadgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER. Laziness is not affected. * FetchType.EAGER (via join fetch or subsequent select). Attributes that are not specified are treated as
* FetchType.LAZY or FetchType.EAGER depending on the attribute's definition in metadata
*/ */
public static final String LOADGRAPH = "javax.persistence.loadgraph"; public static final String LOADGRAPH = "javax.persistence.loadgraph";

View File

@ -101,14 +101,18 @@ public class QueryHints {
public static final String HINT_NATIVE_LOCKMODE = NATIVE_LOCKMODE; public static final String HINT_NATIVE_LOCKMODE = NATIVE_LOCKMODE;
/** /**
* Hint providing an EntityGraph. With JPQL/HQL, the sole functionality is attribute nodes are treated as * Hint providing a "fetchgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER. Laziness is not affected. * FetchType.EAGER (via join fetch or subsequent select).
*
* Note: Currently, attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending
* on the attribute's definition in metadata, rather than forcing FetchType.LAZY.
*/ */
public static final String HINT_FETCHGRAPH = FETCHGRAPH; public static final String HINT_FETCHGRAPH = FETCHGRAPH;
/** /**
* Hint providing an EntityGraph. With JPQL/HQL, the sole functionality is attribute nodes are treated as * Hint providing a "loadgraph" EntityGraph. Attributes explicitly specified as AttributeNodes are treated as
* FetchType.EAGER. Laziness is not affected. * FetchType.EAGER (via join fetch or subsequent select). Attributes that are not specified are treated as
* FetchType.LAZY or FetchType.EAGER depending on the attribute's definition in metadata
*/ */
public static final String HINT_LOADGRAPH = LOADGRAPH; public static final String HINT_LOADGRAPH = LOADGRAPH;

View File

@ -52,8 +52,12 @@ import org.junit.Test;
*/ */
public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCase { public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCase {
// TODO: Currently, "loadgraph" and "fetchgraph" operate identically in JPQL. The spec states that "fetchgraph"
// shall use LAZY for non-specified attributes, ignoring their metadata. Changes to ToOne select vs. join,
// allowing queries to force laziness, etc. will require changes here and impl logic.
@Test @Test
public void testQueryHintEntityGraph() { public void testLoadGraph() {
EntityManager entityManager = getOrCreateEntityManager(); EntityManager entityManager = getOrCreateEntityManager();
entityManager.getTransaction().begin(); entityManager.getTransaction().begin();
@ -61,7 +65,7 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
entityGraph.addAttributeNodes( "location" ); entityGraph.addAttributeNodes( "location" );
entityGraph.addAttributeNodes( "markets" ); entityGraph.addAttributeNodes( "markets" );
Query query = entityManager.createQuery( "from " + Company.class.getName() ); Query query = entityManager.createQuery( "from " + Company.class.getName() );
query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph ); query.setHint( QueryHints.HINT_LOADGRAPH, entityGraph );
Company company = (Company) query.getSingleResult(); Company company = (Company) query.getSingleResult();
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
@ -70,6 +74,9 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
assertFalse( Hibernate.isInitialized( company.employees ) ); assertFalse( Hibernate.isInitialized( company.employees ) );
assertTrue( Hibernate.isInitialized( company.location ) ); assertTrue( Hibernate.isInitialized( company.location ) );
assertTrue( Hibernate.isInitialized( company.markets ) ); assertTrue( Hibernate.isInitialized( company.markets ) );
// With "loadgraph", non-specified attributes use the fetch modes defined in the mappings. So, here,
// @ElementCollection(fetch = FetchType.EAGER) should cause the follow-on selects to happen.
assertTrue( Hibernate.isInitialized( company.phoneNumbers ) );
entityManager = getOrCreateEntityManager(); entityManager = getOrCreateEntityManager();
entityManager.getTransaction().begin(); entityManager.getTransaction().begin();
@ -79,7 +86,7 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
Subgraph<Employee> subgraph2 = subgraph1.addSubgraph( "managers" ); Subgraph<Employee> subgraph2 = subgraph1.addSubgraph( "managers" );
subgraph2.addAttributeNodes( "managers" ); subgraph2.addAttributeNodes( "managers" );
query = entityManager.createQuery( "from " + Company.class.getName() ); query = entityManager.createQuery( "from " + Company.class.getName() );
query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph ); query.setHint( QueryHints.HINT_LOADGRAPH, entityGraph );
company = (Company) query.getSingleResult(); company = (Company) query.getSingleResult();
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
@ -88,6 +95,9 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
assertTrue( Hibernate.isInitialized( company.employees ) ); assertTrue( Hibernate.isInitialized( company.employees ) );
assertTrue( Hibernate.isInitialized( company.location ) ); assertTrue( Hibernate.isInitialized( company.location ) );
assertTrue( Hibernate.isInitialized( company.markets ) ); assertTrue( Hibernate.isInitialized( company.markets ) );
// With "loadgraph", non-specified attributes use the fetch modes defined in the mappings. So, here,
// @ElementCollection(fetch = FetchType.EAGER) should cause the follow-on selects to happen.
assertTrue( Hibernate.isInitialized( company.phoneNumbers ) );
boolean foundManager = false; boolean foundManager = false;
Iterator<Employee> employeeItr = company.employees.iterator(); Iterator<Employee> employeeItr = company.employees.iterator();
@ -104,7 +114,7 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
} }
@Test @Test
public void testQueryHintEntityGraphWithExplicitFetch() { public void testEntityGraphWithExplicitFetch() {
EntityManager entityManager = getOrCreateEntityManager(); EntityManager entityManager = getOrCreateEntityManager();
entityManager.getTransaction().begin(); entityManager.getTransaction().begin();
@ -112,9 +122,10 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
entityGraph.addAttributeNodes( "location" ); entityGraph.addAttributeNodes( "location" );
entityGraph.addAttributeNodes( "markets" ); entityGraph.addAttributeNodes( "markets" );
entityGraph.addAttributeNodes( "employees" ); entityGraph.addAttributeNodes( "employees" );
// Ensure the EntityGraph and explicit fetches do not conflict.
Query query = entityManager.createQuery( "from " + Company.class.getName() Query query = entityManager.createQuery( "from " + Company.class.getName()
+ " as c left join fetch c.location left join fetch c.employees" ); + " as c left join fetch c.location left join fetch c.employees" );
query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph ); query.setHint( QueryHints.HINT_LOADGRAPH, entityGraph );
Company company = (Company) query.getSingleResult(); Company company = (Company) query.getSingleResult();
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
@ -123,6 +134,9 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
assertTrue( Hibernate.isInitialized( company.employees ) ); assertTrue( Hibernate.isInitialized( company.employees ) );
assertTrue( Hibernate.isInitialized( company.location ) ); assertTrue( Hibernate.isInitialized( company.location ) );
assertTrue( Hibernate.isInitialized( company.markets ) ); assertTrue( Hibernate.isInitialized( company.markets ) );
// With "loadgraph", non-specified attributes use the fetch modes defined in the mappings. So, here,
// @ElementCollection(fetch = FetchType.EAGER) should cause the follow-on selects to happen.
assertTrue( Hibernate.isInitialized( company.phoneNumbers ) );
} }
@Before @Before
@ -157,6 +171,9 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
company.markets = new HashSet<Market>(); company.markets = new HashSet<Market>();
company.markets.add( Market.SERVICES ); company.markets.add( Market.SERVICES );
company.markets.add( Market.TECHNOLOGY ); company.markets.add( Market.TECHNOLOGY );
company.phoneNumbers = new HashSet<String>();
company.phoneNumbers.add( "012-345-6789" );
company.phoneNumbers.add( "987-654-3210" );
entityManager.persist( company ); entityManager.persist( company );
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
@ -181,6 +198,9 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
@ElementCollection @ElementCollection
public Set<Market> markets; public Set<Market> markets;
@ElementCollection(fetch = FetchType.EAGER)
public Set<String> phoneNumbers;
} }
@Entity @Entity