/******************************************************************************* * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * 05/19/2010-2.1 ailitchev - Bug 244124 - Add Nested FetchGroup ******************************************************************************/ package org.eclipse.persistence.testing.tests.jpa.fieldaccess.fetchgroups; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import junit.framework.TestSuite; import org.eclipse.persistence.config.DescriptorCustomizer; import org.eclipse.persistence.config.QueryHints; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.queries.FetchGroup; import org.eclipse.persistence.queries.FetchGroupTracker; import org.eclipse.persistence.testing.models.jpa.fieldaccess.advanced.Employee; import org.eclipse.persistence.testing.models.jpa.fieldaccess.advanced.PhoneNumber; import org.junit.Test; /** * Simple tests to verify the functionality of single level FetchGroup usage * * @author dclarke * @since EclipseLink 2.1 */ public class SimpleDefaultFetchGroupTests extends BaseFetchGroupTests { public SimpleDefaultFetchGroupTests() { super(); } public SimpleDefaultFetchGroupTests(String name) { super(name); } public static junit.framework.Test suite() { TestSuite suite = new TestSuite(); suite.setName("SimpleDefaultFetchGroupTests"); suite.addTest(new SimpleDefaultFetchGroupTests("testSetup")); suite.addTest(new SimpleDefaultFetchGroupTests("findDefaultFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("singleResultDefaultFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("resultListDefaultFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("resultListWithJoinFetchAddress")); suite.addTest(new SimpleDefaultFetchGroupTests("resultListWithJoinFetchAddress_AddressInFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("singleResultNoFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("resultListNoFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("emptyFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("managerFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("employeeNamesFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("namedEmptyFetchGroupUsingGetSingleResult")); suite.addTest(new SimpleDefaultFetchGroupTests("namedNamesFetchGroupUsingGetSingleResult")); suite.addTest(new SimpleDefaultFetchGroupTests("joinFetchEmployeeAddressWithDynamicFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("joinFetchEmployeeAddressPhoneWithDynamicFetchGroup")); suite.addTest(new SimpleDefaultFetchGroupTests("joinFetchEmployeeAddressPhoneWithDynamicFetchGroup_AddressInFetchGroup")); return suite; } /** * Set default fetch groups. * * @see EmployeeCustomizer * @see PhoneCustomizer */ public void setUp() { super.setUp(); try { (new EmployeeCustomizer()).customize(employeeDescriptor); (new PhoneCustomizer()).customize(phoneDescriptor); // reprepare read queries after all fetch groups set into all descriptors. reprepareReadQueries(employeeDescriptor); reprepareReadQueries(phoneDescriptor); } catch (RuntimeException rtEx) { throw rtEx; } catch (Exception ex) { throw new RuntimeException(ex); } FetchGroup defaultEmpFG = employeeDescriptor.getDefaultFetchGroup(); FetchGroup defaultPhoneFG = phoneDescriptor.getDefaultFetchGroup(); assertConfig(employeeDescriptor, defaultEmpFG, 0); assertConfig(phoneDescriptor, defaultPhoneFG, 0); } @Test public void findDefaultFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Employee emp = minimumEmployee(em); assertNotNull(emp); assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertDefaultFetched(emp); assertNotFetchedAttribute(emp, "salary"); emp.getSalary(); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertFetchedAttribute(emp, "salary"); assertNoFetchGroup(emp.getAddress()); assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); } assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); if (emp.getManager() != null) { assertDefaultFetched(emp.getManager()); assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } else { // If manager_id field is null then getManager() does not trigger an sql. assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void singleResultDefaultFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); Employee emp = (Employee) query.getSingleResult(); assertNotNull(emp); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertDefaultFetched(emp); emp.getSalary(); assertFetchedAttribute(emp, "salary"); assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp.getAddress()); assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); } assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); if (emp.getManager() != null) { assertDefaultFetched(emp.getManager()); assertEquals(6, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } else { // If manager_id field is null then getManager() does not trigger an sql. assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void resultListDefaultFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); List<Employee> emps = query.getResultList(); assertNotNull(emps); assertEquals(1, emps.size()); Employee emp = emps.get(0); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertDefaultFetched(emp); emp.getSalary(); assertNoFetchGroup(emp); assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp.getAddress()); assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); } assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); if (emp.getManager() != null) { assertDefaultFetched(emp.getManager()); assertEquals(6, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } else { // If manager_id field is null then getManager() does not trigger an sql. assertEquals(5, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void resultListWithJoinFetchAddress() { internalResultListWithJoinFetchAddress(false); } @Test public void resultListWithJoinFetchAddress_AddressInFetchGroup() { internalResultListWithJoinFetchAddress(true); } void internalResultListWithJoinFetchAddress(boolean addAddressToFetchGroup) { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Query query = em.createQuery("SELECT e FROM Employee e JOIN FETCH e.address WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); FetchGroup fg = null; if(addAddressToFetchGroup) { // that returns clone of the default fetch group fg = employeeDescriptor.getFetchGroupManager().createDefaultFetchGroup(); fg.addAttribute("address"); query.setHint(QueryHints.FETCH_GROUP, fg); } Employee emp = (Employee)query.getSingleResult(); int nSql = 2; if(!addAddressToFetchGroup) { // An extra sql to read employee's Address - // because address attribute is not in the fetch group the Address object was not built // by join fetch - though the db row for address was read in. // // yet another extra sql generated when the whole employee object is read when address is set. nSql = nSql + 2; } assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); if(addAddressToFetchGroup) { assertFetched(emp, fg); } else { // the whole object has been instantiated when address has been set assertNoFetchGroup(emp); } // instantiates the whole object - unless already instantiated emp.getSalary(); assertNoFetchGroup(emp); if(addAddressToFetchGroup) { nSql++; } assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp.getAddress()); assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); } assertEquals(++nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); if (emp.getManager() != null) { assertDefaultFetched(emp.getManager()); assertEquals(++nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } else { // If manager_id field is null then getManager() does not trigger an sql. assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void singleResultNoFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); assertNotNull(employeeDescriptor.getFetchGroupManager().getDefaultFetchGroup()); query.setHint(QueryHints.FETCH_GROUP_DEFAULT, "false"); assertNull(getFetchGroup(query)); Employee emp = (Employee) query.getSingleResult(); assertNotNull(emp); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp); assertNoFetchGroup(emp.getAddress()); assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); } assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void resultListNoFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); query.setHint(QueryHints.FETCH_GROUP_DEFAULT, "false"); List<Employee> emps = query.getResultList(); assertNotNull(emps); assertEquals(1, emps.size()); Employee emp = emps.get(0); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp); assertNoFetchGroup(emp.getAddress()); assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); } assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void emptyFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); // Use q query since find will only use default fetch group Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); FetchGroup emptyFG = new FetchGroup("empty@" + System.currentTimeMillis()); query.setHint(QueryHints.FETCH_GROUP, emptyFG); assertEquals(emptyFG, getFetchGroup(query)); Employee emp = (Employee) query.getSingleResult(); assertFetched(emp, emptyFG); for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); phone.getAreaCode(); assertNoFetchGroup(phone); } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void managerFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); //Use q query since find will only use default fetch group //Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); //query.setParameter("ID", minimumEmployeeId(em)); //Complex where clause used to avoid triggering employees and their departments: // Don't include employees who are managers themselves - otherwise if first selected as employee, then as e.manager the full read will be triggered; // Don't include managers with departments - because there is no fetch group on e.manager its (non-null) department will trigger an extra sql Query query = em.createQuery("SELECT e FROM Employee e WHERE e.manager IS NOT NULL AND NOT EXISTS(SELECT e2 FROM Employee e2 WHERE e2.manager = e) AND e.manager.department IS NULL"); FetchGroup managerFG = new FetchGroup(); managerFG.addAttribute("manager"); query.setHint(QueryHints.FETCH_GROUP, managerFG); query.setHint(QueryHints.LEFT_FETCH, "e.manager"); assertEquals(managerFG, getFetchGroup(query)); List employees = query.getResultList(); Employee emp = (Employee)employees.get(0); assertFetched(emp, managerFG); assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); emp.getManager(); assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertFetched(emp, managerFG); emp.getLastName(); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp); int numPhones = 0; for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); phone.getAreaCode(); assertNoFetchGroup(phone); numPhones++; } // 1 sql to read all the phones with a fetch group + 1 sql per phone to re-read each phone without fetch group assertEquals(3 + numPhones, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void employeeNamesFetchGroup() throws Exception { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); int minId = minimumEmployeeId(em); assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); // Use q query since find will only use default fetch group Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minId); FetchGroup namesFG = new FetchGroup(); namesFG.addAttribute("firstName"); namesFG.addAttribute("lastName"); query.setHint(QueryHints.FETCH_GROUP, namesFG); assertNotNull(getFetchGroup(query)); assertSame(namesFG, getFetchGroup(query)); Employee emp = (Employee) query.getSingleResult(); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertFetched(emp, namesFG); emp.getId(); emp.getFirstName(); emp.getLastName(); emp.getVersion(); assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertFetched(emp, namesFG); emp.getGender(); emp.getSalary(); assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertNoFetchGroup(emp); int numPhones = 0; for (PhoneNumber phone : emp.getPhoneNumbers()) { assertDefaultFetched(phone); phone.getAreaCode(); assertNoFetchGroup(phone); numPhones++; } // 1 sql to read all the phones with a fetch group + 1 sql per phone to re-read each phone without fetch group assertEquals(4 + numPhones, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); if(emp.getManager() != null) { assertEquals(5 + numPhones, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); assertDefaultFetched(emp.getManager()); } else { // If manager_id field is null then getManager() does not trigger an sql. assertEquals(4 + numPhones, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } @Test public void namedEmptyFetchGroupUsingGetSingleResult() throws Exception { FetchGroup fetchGroup = new FetchGroup("test"); employeeDescriptor.getFetchGroupManager().addFetchGroup(fetchGroup); assertTrue(fetchGroup.getItems().isEmpty()); assertEquals(1, employeeDescriptor.getFetchGroupManager().getFetchGroups().size()); EntityManager em = createEntityManager("fieldaccess"); Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); query.setHint(QueryHints.FETCH_GROUP_NAME, "test"); Employee emp = (Employee) query.getSingleResult(); assertNotNull(emp); assertFetched(emp, "test"); } @Test public void namedNamesFetchGroupUsingGetSingleResult() throws Exception { FetchGroup fetchGroup = new FetchGroup("names"); fetchGroup.addAttribute("firstName"); fetchGroup.addAttribute("lastName"); employeeDescriptor.getFetchGroupManager().addFetchGroup(fetchGroup); assertEquals(2, fetchGroup.getItems().size()); assertEquals(1, employeeDescriptor.getFetchGroupManager().getFetchGroups().size()); EntityManager em = createEntityManager("fieldaccess"); Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", minimumEmployeeId(em)); query.setHint(QueryHints.FETCH_GROUP_NAME, "names"); Employee emp = (Employee) query.getSingleResult(); assertNotNull(emp); FetchGroupTracker tracker = (FetchGroupTracker) emp; assertNotNull(tracker); FetchGroup usedFG = tracker._persistence_getFetchGroup(); assertNotNull("No FetchGroup found on read Employee", fetchGroup); // No longer can make any assumptions about a name of FetchGroup on the entity // What's guaranteed is that attribute sets are the same assertTrue(fetchGroup.getAttributeNames().equals(usedFG.getAttributeNames())); // assertEquals(fetchGroup.getName(), usedFG.getName()); // assertSame(fetchGroup, ((EntityFetchGroup)usedFG).getParent()); assertEquals(4, fetchGroup.getItems().size()); assertTrue(tracker._persistence_isAttributeFetched("id")); assertTrue(tracker._persistence_isAttributeFetched("version")); assertFalse(tracker._persistence_isAttributeFetched("salary")); assertTrue(tracker._persistence_isAttributeFetched("firstName")); assertTrue(tracker._persistence_isAttributeFetched("lastName")); assertFalse(tracker._persistence_isAttributeFetched("department")); } @Test public void joinFetchEmployeeAddressWithDynamicFetchGroup() { EntityManager em = createEntityManager("fieldaccess"); Query query = em.createQuery("SELECT e FROM Employee e JOIN FETCH e.address"); FetchGroup fetchGroup = new FetchGroup("names"); fetchGroup.addAttribute("firstName"); fetchGroup.addAttribute("lastName"); query.setHint(QueryHints.FETCH_GROUP, fetchGroup); List<Employee> emps = query.getResultList(); assertNotNull(emps); } @Test public void joinFetchEmployeeAddressPhoneWithDynamicFetchGroup() { internalJoinFetchEmployeeAddressPhoneWithDynamicFetchGroup(false); } @Test public void joinFetchEmployeeAddressPhoneWithDynamicFetchGroup_AddressInFetchGroup() { internalJoinFetchEmployeeAddressPhoneWithDynamicFetchGroup(true); } void internalJoinFetchEmployeeAddressPhoneWithDynamicFetchGroup(boolean addAddressToFetchGroup) { EntityManager em = createEntityManager("fieldaccess"); try { beginTransaction(em); Query query = em.createQuery("SELECT e FROM Employee e JOIN FETCH e.address WHERE e.id IN (SELECT p.owner.id FROM PhoneNumber p)"); FetchGroup fetchGroup = new FetchGroup("names"); fetchGroup.addAttribute("firstName"); fetchGroup.addAttribute("lastName"); if(addAddressToFetchGroup) { fetchGroup.addAttribute("address"); } query.setHint(QueryHints.FETCH_GROUP, fetchGroup); List<Employee> emps = query.getResultList(); assertNotNull(emps); if(addAddressToFetchGroup) { assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } else { assertEquals(1 + 2*emps.size(), getQuerySQLTracker(em).getTotalSQLSELECTCalls()); } for (Employee emp : emps) { if(addAddressToFetchGroup) { assertFetched(emp, fetchGroup); } else { // the whole object has been instantiated when address has been set assertNoFetchGroup(emp); } } } finally { if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); } } public static class EmployeeCustomizer implements DescriptorCustomizer { public void customize(ClassDescriptor descriptor) throws Exception { FetchGroup fg = new FetchGroup("Employee-default"); fg.addAttribute("firstName"); fg.addAttribute("lastName"); descriptor.getFetchGroupManager().setDefaultFetchGroup(fg); } } /* public static class PhoneCustomizer implements DescriptorCustomizer { public void customize(ClassDescriptor descriptor) throws Exception { FetchGroup fg = new FetchGroup("Phone-default"); fg.addAttribute("number"); if (!descriptor.hasFetchGroupManager()) { descriptor.setFetchGroupManager(new FetchGroupManager()); } descriptor.getFetchGroupManager().setDefaultFetchGroup(fg); } }*/ }