| /* |
| * Copyright (c) 2011, 2021 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // 05/19/2010-2.1 ailitchev - Bug 244124 - Add Nested FetchGroup |
| // 09/21/2010-2.2 Frank Schwarz and ailitchev - Bug 325684 - QueryHints.BATCH combined with QueryHints.FETCH_GROUP_LOAD will cause NPE |
| package org.eclipse.persistence.testing.tests.jpa.fetchgroups; |
| |
| import java.util.Collection; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import jakarta.persistence.EntityManager; |
| import jakarta.persistence.Query; |
| |
| import junit.framework.TestSuite; |
| |
| import org.eclipse.persistence.config.QueryHints; |
| import org.eclipse.persistence.internal.helper.IdentityHashSet; |
| import org.eclipse.persistence.internal.jpa.EntityManagerImpl; |
| import org.eclipse.persistence.internal.queries.EntityFetchGroup; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.jpa.JpaHelper; |
| import org.eclipse.persistence.queries.AttributeGroup; |
| import org.eclipse.persistence.queries.FetchGroup; |
| import org.eclipse.persistence.queries.FetchGroupTracker; |
| import org.eclipse.persistence.queries.LoadGroup; |
| import org.eclipse.persistence.testing.models.jpa.advanced.Address; |
| import org.eclipse.persistence.testing.models.jpa.advanced.Department; |
| import org.eclipse.persistence.testing.models.jpa.advanced.Employee; |
| import org.eclipse.persistence.testing.models.jpa.advanced.PhoneNumber; |
| import org.eclipse.persistence.testing.models.jpa.advanced.Project; |
| import org.eclipse.persistence.testing.models.jpa.advanced.Employee.Gender; |
| import org.eclipse.persistence.testing.models.jpa.advanced.compositepk.Competency; |
| |
| import org.junit.Test; |
| |
| /** |
| * @author dclarke |
| * @since EclipseLink 2.1 |
| */ |
| public class NestedFetchGroupTests extends BaseFetchGroupTests { |
| |
| public NestedFetchGroupTests() { |
| super(); |
| } |
| |
| public NestedFetchGroupTests(String name) { |
| super(name); |
| } |
| |
| public static junit.framework.Test suite() { |
| TestSuite suite = new TestSuite(); |
| suite.setName("NestedFetchGroupTests"); |
| |
| suite.addTest(new NestedFetchGroupTests("testSetup")); |
| suite.addTest(new NestedFetchGroupTests("dynamicFetchGroup_EmployeeAddress")); |
| suite.addTest(new NestedFetchGroupTests("dynamicFetchGroup_Employee_NullAddress")); |
| suite.addTest(new NestedFetchGroupTests("dynamicFetchGroup_EmployeeAddressNullPhone")); |
| suite.addTest(new NestedFetchGroupTests("dynamicFetchGroup_EmployeeAddressEmptyPhone")); |
| suite.addTest(new NestedFetchGroupTests("dynamicFetchGroup_EmployeeAddressEmptyPhoneLoad")); |
| suite.addTest(new NestedFetchGroupTests("dynamicHierarchicalFetchGroup")); |
| suite.addTest(new NestedFetchGroupTests("dynamicFetchGroup_ElementCollection")); |
| //**temp suite.addTest(new NestedFetchGroupTests("dynamicHierarchicalFetchGroup_JOIN_FETCH")); |
| suite.addTest(new NestedFetchGroupTests("dynamicHierarchicalFetchGroup_JOIN_FETCH_Copy")); |
| //**temp suite.addTest(new NestedFetchGroupTests("managerDoubleNestedFetchGroupWithJoinFetch")); |
| suite.addTest(new NestedFetchGroupTests("managerTripleNestedFetchGroupWithJoinFetch")); |
| suite.addTest(new NestedFetchGroupTests("allNestedFetchGroupWithJoinFetch")); |
| suite.addTest(new NestedFetchGroupTests("joinFetchDefaultFetchGroup")); |
| suite.addTest(new NestedFetchGroupTests("joinFetchOutsideOfFetchGroup")); |
| suite.addTest(new NestedFetchGroupTests("simpleNestedFetchGroupWithBatch")); |
| suite.addTest(new NestedFetchGroupTests("simpleLoadGroup")); |
| suite.addTest(new NestedFetchGroupTests("simpleFetchGroupLoadWithBatch")); |
| |
| return suite; |
| } |
| |
| @Override |
| public void setUp() { |
| super.setUp(); |
| |
| defaultPhoneFG = new FetchGroup(); |
| defaultPhoneFG.addAttribute("number"); |
| phoneDescriptor.getFetchGroupManager().setDefaultFetchGroup(defaultPhoneFG); |
| |
| reprepareReadQueries(phoneDescriptor); |
| reprepareReadQueries(employeeDescriptor); |
| |
| // We'll put a default FetchGroup on Phone |
| assertNotNull(phoneDescriptor.getDefaultFetchGroup()); |
| assertNotNull(phoneDescriptor.getDescriptorQueryManager().getReadObjectQuery().getExecutionFetchGroup()); |
| assertTrue(phoneDescriptor.getFetchGroupManager().getFetchGroups().isEmpty()); |
| } |
| |
| @Test |
| public void dynamicFetchGroup_EmployeeAddress() throws Exception { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); |
| query.setParameter("GENDER", Gender.Male); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup fg = new FetchGroup(); |
| fg.addAttribute("id"); |
| fg.addAttribute("version"); |
| fg.addAttribute("firstName"); |
| fg.addAttribute("lastName"); |
| fg.addAttribute("address.city"); |
| fg.addAttribute("address.postalCode"); |
| |
| // Configure the dynamic FetchGroup |
| query.setHint(QueryHints.FETCH_GROUP, fg); |
| |
| List<Employee> emps = query.getResultList(); |
| |
| assertNotNull(emps); |
| for (Employee emp : emps) { |
| FetchGroupTracker tracker = (FetchGroupTracker) emp; |
| |
| assertNotNull(tracker._persistence_getFetchGroup()); |
| |
| // Verify specified fields plus mandatory ones are loaded |
| assertTrue(tracker._persistence_isAttributeFetched("firstName")); |
| assertTrue(tracker._persistence_isAttributeFetched("lastName")); |
| assertTrue(tracker._persistence_isAttributeFetched("address")); |
| FetchGroupTracker addrTracker = (FetchGroupTracker) emp.getAddress(); |
| assertTrue(addrTracker._persistence_isAttributeFetched("city")); |
| assertTrue(addrTracker._persistence_isAttributeFetched("postalCode")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("street")); |
| |
| // Verify the other fields are not loaded |
| assertFalse(tracker._persistence_isAttributeFetched("salary")); |
| assertFalse(tracker._persistence_isAttributeFetched("startTime")); |
| assertFalse(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Force the loading of lazy fields and verify |
| emp.getSalary(); |
| |
| assertTrue(tracker._persistence_isAttributeFetched("firstName")); |
| assertTrue(tracker._persistence_isAttributeFetched("lastName")); |
| assertTrue(tracker._persistence_isAttributeFetched("address")); |
| assertTrue(tracker._persistence_isAttributeFetched("salary")); |
| assertTrue(tracker._persistence_isAttributeFetched("startTime")); |
| assertTrue(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Now we'll check the address uses the provided dynamic fetch-group |
| addrTracker = (FetchGroupTracker) emp.getAddress(); |
| assertNotNull("Address does not have a FetchGroup", addrTracker._persistence_getFetchGroup()); |
| assertTrue(addrTracker._persistence_isAttributeFetched("city")); |
| assertTrue(addrTracker._persistence_isAttributeFetched("postalCode")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("street")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("country")); |
| |
| // Now we'll check the phoneNumbers use of the default fetch group |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| FetchGroupTracker phoneTracker = (FetchGroupTracker) phone; |
| assertNotNull("PhoneNumber does not have a FetchGroup", phoneTracker._persistence_getFetchGroup()); |
| assertTrue(phoneTracker._persistence_isAttributeFetched("number")); |
| assertFalse(phoneTracker._persistence_isAttributeFetched("areaCode")); |
| } |
| } |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void dynamicFetchGroup_Employee_NullAddress() throws Exception { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); |
| query.setParameter("GENDER", Gender.Male); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup empGroup = new FetchGroup(); |
| empGroup.addAttribute("firstName"); |
| empGroup.addAttribute("lastName"); |
| empGroup.addAttribute("address"); |
| |
| // Define the fields to be fetched on Address |
| FetchGroup addressGroup = new FetchGroup(); |
| addressGroup.addAttribute("city"); |
| addressGroup.addAttribute("postalCode"); |
| |
| empGroup.addAttribute("address"); |
| |
| // Configure the dynamic FetchGroup |
| query.setHint(QueryHints.FETCH_GROUP, empGroup); |
| |
| List<Employee> emps = query.getResultList(); |
| |
| assertNotNull(emps); |
| for (Employee emp : emps) { |
| FetchGroupTracker tracker = (FetchGroupTracker) emp; |
| |
| assertNotNull(tracker._persistence_getFetchGroup()); |
| |
| // Verify specified fields plus mandatory ones are loaded |
| assertTrue(tracker._persistence_isAttributeFetched("id")); |
| assertTrue(tracker._persistence_isAttributeFetched("firstName")); |
| assertTrue(tracker._persistence_isAttributeFetched("lastName")); |
| assertTrue(tracker._persistence_isAttributeFetched("version")); |
| |
| // Verify the other fields are not loaded |
| assertFalse(tracker._persistence_isAttributeFetched("salary")); |
| assertFalse(tracker._persistence_isAttributeFetched("startTime")); |
| assertFalse(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Force the loading of lazy fields and verify |
| emp.getSalary(); |
| |
| assertTrue(tracker._persistence_isAttributeFetched("salary")); |
| assertTrue(tracker._persistence_isAttributeFetched("startTime")); |
| assertTrue(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Now we'll check the address uses the provided dynamic fetch-group |
| FetchGroupTracker addrTracker = (FetchGroupTracker) emp.getAddress(); |
| assertNull("Address has an unexpected FetchGroup", addrTracker._persistence_getFetchGroup()); |
| |
| // Now we'll check the phoneNumbers use of the default fetch group |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| FetchGroupTracker phoneTracker = (FetchGroupTracker) phone; |
| assertNotNull("PhoneNumber does not have a FetchGroup", phoneTracker._persistence_getFetchGroup()); |
| assertTrue(phoneTracker._persistence_isAttributeFetched("number")); |
| assertFalse(phoneTracker._persistence_isAttributeFetched("areaCode")); |
| } |
| } |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void dynamicFetchGroup_EmployeeAddressNullPhone() throws Exception { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); |
| query.setParameter("GENDER", Gender.Male); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup empGroup = new FetchGroup(); |
| empGroup.addAttribute("firstName"); |
| empGroup.addAttribute("lastName"); |
| empGroup.addAttribute("address"); |
| empGroup.addAttribute("address.city"); |
| empGroup.addAttribute("address.postalCode"); |
| |
| //empGroup.addAttribute("phoneNumbers").setUseDefaultFetchGroup(false); |
| FetchGroup fullPhone = this.phoneDescriptor.getFetchGroupManager().createFullFetchGroup(); |
| // to preclude Employee from being loaded by phoneNumber.owner add it to the fetch group |
| fullPhone.addAttribute("owner.id"); |
| empGroup.addAttribute("phoneNumbers", fullPhone); |
| |
| // Configure the dynamic FetchGroup |
| query.setHint(QueryHints.FETCH_GROUP, empGroup); |
| |
| List<Employee> emps = query.getResultList(); |
| |
| assertNotNull(emps); |
| for (Employee emp : emps) { |
| FetchGroupTracker tracker = (FetchGroupTracker) emp; |
| |
| assertNotNull(tracker._persistence_getFetchGroup()); |
| |
| // Verify specified fields plus mandatory ones are loaded |
| assertTrue(tracker._persistence_isAttributeFetched("id")); |
| assertTrue(tracker._persistence_isAttributeFetched("firstName")); |
| assertTrue(tracker._persistence_isAttributeFetched("lastName")); |
| assertTrue(tracker._persistence_isAttributeFetched("version")); |
| |
| // Verify the other fields are not loaded |
| assertFalse(tracker._persistence_isAttributeFetched("salary")); |
| assertFalse(tracker._persistence_isAttributeFetched("startTime")); |
| assertFalse(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Force the loading of lazy fields and verify |
| emp.getSalary(); |
| |
| assertTrue(tracker._persistence_isAttributeFetched("salary")); |
| assertTrue(tracker._persistence_isAttributeFetched("startTime")); |
| assertTrue(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Now we'll check the address uses the provided dynamic fetch-group |
| FetchGroupTracker addrTracker = (FetchGroupTracker) emp.getAddress(); |
| assertNotNull("Address does not have a FetchGroup", addrTracker._persistence_getFetchGroup()); |
| assertTrue(addrTracker._persistence_isAttributeFetched("city")); |
| assertTrue(addrTracker._persistence_isAttributeFetched("postalCode")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("street")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("country")); |
| |
| // Now we'll check the phoneNumbers use of the default fetch group |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| FetchGroupTracker phoneTracker = (FetchGroupTracker) phone; |
| assertNull("PhoneNumber has a FetchGroup", phoneTracker._persistence_getFetchGroup()); |
| } |
| } |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void dynamicFetchGroup_ElementCollection(){ |
| EntityManager em = createEntityManager(); |
| AttributeGroup compt = new AttributeGroup(null, Competency.class, true); |
| compt.addAttribute("description"); |
| AttributeGroup fg = new AttributeGroup(null, org.eclipse.persistence.testing.models.jpa.advanced.compositepk.Department.class, true); |
| fg.addAttribute("competencies", compt); |
| clearCache(); |
| Collection<org.eclipse.persistence.testing.models.jpa.advanced.compositepk.Department> results = em.createQuery("select d from Department d").setHint(QueryHints.FETCH_GROUP, fg.toFetchGroup()).getResultList(); |
| for (org.eclipse.persistence.testing.models.jpa.advanced.compositepk.Department dept : results){ |
| assertFalse("Collection fetched: scientists, fg ignored", ((FetchGroupTracker)dept)._persistence_isAttributeFetched("scientists")); |
| assertFalse("Collection fetched: offices, fg ignored", ((FetchGroupTracker)dept)._persistence_isAttributeFetched("offices")); |
| assertTrue("Collection not fetched: competencies, fg ignored", ((FetchGroupTracker)dept)._persistence_isAttributeFetched("competencies")); |
| for (Competency embeded: dept.getCompetencies()){ |
| assertTrue("Element attribute not loaded: description, fg ignored", ((FetchGroupTracker)embeded)._persistence_isAttributeFetched("description")); |
| } |
| dept.getScientists().size(); |
| assertTrue("Collection not fetched: scientists, fg ignored", ((FetchGroupTracker)dept)._persistence_isAttributeFetched("scientists")); |
| assertTrue("Collection not fetched: offices, fg ignored", ((FetchGroupTracker)dept)._persistence_isAttributeFetched("offices")); |
| assertTrue("Collection not fetched: competencies, fg ignored", ((FetchGroupTracker)dept)._persistence_isAttributeFetched("competencies")); |
| for (Competency embeded: dept.getCompetencies()){ |
| embeded.getRating(); |
| assertTrue("Element attribute not loaded: description, fg ignored", ((FetchGroupTracker)embeded)._persistence_isAttributeFetched("description")); |
| } |
| } |
| } |
| |
| @Test |
| public void dynamicFetchGroup_EmployeeAddressEmptyPhone() { |
| |
| } |
| public void dynamicFetchGroup_EmployeeAddressEmptyPhoneLoad() { |
| |
| } |
| void internal_dynamicFetchGroup_EmployeeAddressEmptyPhone(boolean shouldLoad) { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); |
| query.setParameter("GENDER", Gender.Male); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup fg = new FetchGroup(); |
| fg.addAttribute("firstName"); |
| fg.addAttribute("lastName"); |
| fg.addAttribute("address.city"); |
| fg.addAttribute("address.postalCode"); |
| // to preclude Employee from being loaded by phoneNumber.owner add it to the fetch group |
| FetchGroup ownerId = new FetchGroup(); |
| ownerId.addAttribute("owner.id"); |
| fg.addAttribute("phoneNumbers", ownerId); |
| |
| if(shouldLoad) { |
| fg.setShouldLoad(true); |
| } |
| |
| // Configure the dynamic FetchGroup |
| query.setHint(QueryHints.FETCH_GROUP, fg); |
| |
| List<Employee> emps = query.getResultList(); |
| |
| assertNotNull(emps); |
| assertEquals(1 + (shouldLoad ? 0 : (emps.size() * 2)), getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| |
| for (Employee emp : emps) { |
| FetchGroupTracker tracker = (FetchGroupTracker) emp; |
| |
| assertNotNull(tracker._persistence_getFetchGroup()); |
| |
| // Verify specified fields plus mandatory ones are loaded |
| assertTrue(tracker._persistence_isAttributeFetched("id")); |
| assertTrue(tracker._persistence_isAttributeFetched("firstName")); |
| assertTrue(tracker._persistence_isAttributeFetched("lastName")); |
| assertTrue(tracker._persistence_isAttributeFetched("version")); |
| |
| // Verify the other fields are not loaded |
| assertFalse(tracker._persistence_isAttributeFetched("salary")); |
| assertFalse(tracker._persistence_isAttributeFetched("startTime")); |
| assertFalse(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Force the loading of lazy fields and verify |
| emp.getSalary(); |
| |
| assertTrue(tracker._persistence_isAttributeFetched("salary")); |
| assertTrue(tracker._persistence_isAttributeFetched("startTime")); |
| assertTrue(tracker._persistence_isAttributeFetched("endTime")); |
| |
| // Now we'll check the address uses the provided dynamic fetch-group |
| FetchGroupTracker addrTracker = (FetchGroupTracker) emp.getAddress(); |
| assertNotNull("Address does not have a FetchGroup", addrTracker._persistence_getFetchGroup()); |
| assertTrue(addrTracker._persistence_isAttributeFetched("city")); |
| assertTrue(addrTracker._persistence_isAttributeFetched("postalCode")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("street")); |
| assertFalse(addrTracker._persistence_isAttributeFetched("country")); |
| |
| // Now we'll check the phoneNumbers use of the default fetch group |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| FetchGroupTracker phoneTracker = (FetchGroupTracker) phone; |
| assertNotNull("PhoneNumber does not have a FetchGroup", phoneTracker._persistence_getFetchGroup()); |
| assertFalse(phoneTracker._persistence_isAttributeFetched("number")); |
| assertFalse(phoneTracker._persistence_isAttributeFetched("areaCode")); |
| |
| phone.getNumber(); |
| |
| assertTrue(phoneTracker._persistence_isAttributeFetched("number")); |
| assertTrue(phoneTracker._persistence_isAttributeFetched("areaCode")); |
| } |
| } |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void dynamicHierarchicalFetchGroup() throws Exception { |
| |
| EntityManager em = createEntityManager(); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.lastName LIKE :LNAME AND e.manager.lastName <> e.lastName"); |
| query.setParameter("LNAME", "%"); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup fg = new FetchGroup(); |
| fg.addAttribute("firstName"); |
| fg.addAttribute("lastName"); |
| fg.addAttribute("salary"); |
| fg.addAttribute("gender"); |
| fg.addAttribute("manager.firstName"); |
| fg.addAttribute("manager.lastName"); |
| fg.addAttribute("manager.salary"); |
| fg.addAttribute("manager.gender"); |
| fg.addAttribute("manager.manager.firstName"); |
| fg.addAttribute("manager.manager.lastName"); |
| fg.addAttribute("manager.manager.salary"); |
| fg.addAttribute("manager.manager.gender"); |
| query.setHint(QueryHints.FETCH_GROUP, fg); |
| |
| List<Employee> emps = query.getResultList(); |
| |
| int numSelect = getQuerySQLTracker(em).getTotalSQLSELECTCalls(); |
| |
| for (Employee emp : emps) { |
| assertFetched(emp, fg); |
| } |
| assertEquals(numSelect, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| } |
| |
| @Test |
| public void dynamicHierarchicalFetchGroup_JOIN_FETCH() throws Exception { |
| internalDynamicHierarchicalFetchGroup_JOIN_FETCH(false); |
| } |
| |
| @Test |
| public void dynamicHierarchicalFetchGroup_JOIN_FETCH_Copy() throws Exception { |
| internalDynamicHierarchicalFetchGroup_JOIN_FETCH(true); |
| } |
| |
| void internalDynamicHierarchicalFetchGroup_JOIN_FETCH(boolean useCopy) throws Exception { |
| |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e JOIN FETCH e.manager WHERE e.lastName LIKE :LNAME AND e.manager.lastName <> e.lastName"); |
| query.setParameter("LNAME", "%"); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup fg = new FetchGroup(); |
| fg.addAttribute("firstName"); |
| fg.addAttribute("lastName"); |
| fg.addAttribute("manager.firstName"); |
| fg.addAttribute("manager.salary"); |
| fg.addAttribute("manager.manager"); |
| query.setHint(QueryHints.FETCH_GROUP, fg); |
| |
| // applied to the selected Employee who is not a manager of some other selected Employee |
| FetchGroup employeeFG = new EntityFetchGroup(new String[]{"id", "version", "firstName", "lastName", "manager"}); |
| // applied to the manager of a selected Employee who is not selected as an Employee |
| FetchGroup managerFG = new EntityFetchGroup(new String[]{"id", "version", "firstName", "salary", "manager"}); |
| // applied to the object which is both selected as an Employee and the manager of another selected Employee |
| FetchGroup employeeManagerFG = employeeDescriptor.getFetchGroupManager().flatUnionFetchGroups(employeeFG, managerFG, false); |
| |
| // used in useCopy case only |
| FetchGroup employeeManagerManagerFG = null; |
| if(useCopy) { |
| employeeManagerManagerFG = employeeDescriptor.getFetchGroupManager().flatUnionFetchGroups(new EntityFetchGroup("manager"), employeeDescriptor.getFetchGroupManager().getNonReferenceEntityFetchGroup(), false); |
| } |
| |
| /* |
| * These are the first names of Employees involved; --> means "managed by". |
| * All the employees here are returned by the query except Jill and Sarah-loo (they got no manager). |
| * |
| * Sarah ------>Bob -------> John ----> Jim-bob ---> Jill ---> null |
| * Charles -----^ Marius ----^ |
| * |
| * Nancy ------> Sarah-loo ---> null |
| * |
| * Sarah, Charles, Nancy should have employeeFG; |
| * Sarah-loo - managerFG; |
| * Bob, Marius - employeeManagerFG; |
| * John, Jim-bob should have a union of three fetch groups: {firstName,lastName,manager}, {firstName,salary,manager}, {manager} |
| * Jill should have a union of two groups: {firstName,salary,manager}, {manager} |
| * The result for all three of them is the same: |
| * in read case (useCopy == false) it should be null (no fetch group), because defaultFetchGroup is null; |
| * in copy case (useCopy == true) it should be a union of "manager" and all non relational attributes (NonReferenceEntityFetchGroup). |
| * That's how leaf reference attribute is treated: |
| * default fetch group for read; |
| * NonReferenceEntityFetchGroup (see FetchGroupManager) for copy. |
| * In this test defaultFetchGroup (null) / NonReferenceEntityFetchGroup comes from {manager}, |
| * in useCopy == true case additional manager comes from another fetch group (they all contain manager). |
| */ |
| |
| List<Employee> emps = query.getResultList(); |
| |
| if(useCopy) { |
| /*for(Employee emp : emps) { |
| int idHashCode = System.identityHashCode(emp); |
| System.out.println(emp.getFirstName() + '\t' + idHashCode); |
| }*/ |
| emps = (List)JpaHelper.getEntityManager(em).copy(emps, fg); |
| } |
| |
| // Sets of managed Employees keyed by their manager |
| Map<Employee, Set<Employee>> managedEmployeesByManager = new IdentityHashMap(); |
| for (Employee emp : emps) { |
| Employee manager = emp.getManager(); |
| Set<Employee> managedEmployees = managedEmployeesByManager.get(manager); |
| if(managedEmployees == null) { |
| managedEmployees = new IdentityHashSet(); |
| managedEmployeesByManager.put(manager, managedEmployees); |
| } |
| managedEmployees.add(emp); |
| } |
| |
| for (Employee emp : emps) { |
| Set<Employee> managedEmployees = managedEmployeesByManager.get(emp); |
| Employee manager = emp.getManager(); |
| if(managedEmployees == null) { |
| // employee is NOT a manager of any of the selected employees: |
| assertFetched(emp, employeeFG); |
| |
| Set<Employee> managedByManagerEmployees = managedEmployeesByManager.get(manager); |
| // indicates whether one of manager's managed employees is a manager itself |
| boolean isManagersManager = false; |
| for(Employee managedEmp : managedByManagerEmployees) { |
| if(managedEmployeesByManager.containsKey(managedEmp)) { |
| isManagersManager = true; |
| break; |
| } |
| } |
| if(isManagersManager) { |
| if(useCopy) { |
| // for at least one of the selected employees manager is manager's manager: |
| // someSelectedEmp.getManager().getManager() == manager |
| // That means for someSelectedEmp emp is defined by {manager.manager} FetchGroup's item, |
| // which means NonReferenceEntityFetchGroup (only non-reference attributes + pk) |
| // for another employee it's just a manager - which means it should include "manager": |
| // employeeManagerManagerFG is the union of these two EntityFetchGroups. |
| assertFetched(manager, employeeManagerManagerFG); |
| } else { |
| // for at least one of the selected employees manager is manager's manager: |
| // someSelectedEmp.getManager().getManager() == manager |
| // That means for someSelectedEmp emp is defined by {manager.manager} FetchGroup's item, |
| // which means no fetch group should be used. |
| assertNoFetchGroup(manager); |
| } |
| } else { |
| // it's not manager's manager |
| if(emps.contains(manager)) { |
| // it's a manager of one of the selected Employees, and selected itself. |
| assertFetched(manager, employeeManagerFG); |
| } else { |
| // it's a manager of one of the selected Employees, but not selected itself. |
| assertFetched(manager, managerFG); |
| } |
| } |
| } else { |
| // employee is a manager of at least one of the selected employees |
| // indicates whether one of emp's managed employees is a manager itself |
| boolean isManagersManager = false; |
| for(Employee managedEmp : managedEmployees) { |
| if(managedEmployeesByManager.containsKey(managedEmp)) { |
| isManagersManager = true; |
| break; |
| } |
| } |
| |
| if(isManagersManager) { |
| if(useCopy) { |
| // for at least one of the selected employees manager is manager's manager: |
| // someSelectedEmp.getManager().getManager() == manager |
| // That means for someSelectedEmp emp is defined by {manager.manager} FetchGroup's item, |
| // which means NonReferenceEntityFetchGroup (only non-reference attributes + pk) |
| // for another employee it's just a manager - which means it should include "manager": |
| // employeeManagerManagerFG is the union of these two EntityFetchGroups. |
| assertFetched(emp, employeeManagerManagerFG); |
| } else { |
| // for at least one of the selected employees emp is manager's manager: |
| // someSelectedEmp.getManager().getManager() == emp |
| // That means for someSelectedEmp emp is defined by {manager.manager} FetchGroup's item, |
| // which means no fetch group should be used. |
| assertNoFetchGroup(emp); |
| } |
| } else { |
| // it's selected employee, manager of some selected employee, but not manager's manager |
| assertFetched(emp, employeeManagerFG); |
| } |
| |
| if(useCopy) { |
| // for at least one of the selected employees manager is manager's manager: |
| // someSelectedEmp.getManager().getManager() == manager |
| // That means for someSelectedEmp emp is defined by {manager.manager} FetchGroup's item, |
| // which means NonReferenceEntityFetchGroup (only non-reference attributes + pk) |
| // for another employee it's just a manager - which means it should include "manager": |
| // employeeManagerManagerFG is the union of these two EntityFetchGroups. |
| assertFetched(manager, employeeManagerManagerFG); |
| } else { |
| // for at least one of the selected employees manager is manager's manager: |
| // someSelectedEmp.getManager().getManager() == manager |
| // That means for someSelectedEmp emp.getManager() is defined by {manager.manager} FetchGroup's item, |
| // which means no fetch group should be used. |
| assertNoFetchGroup(manager); |
| } |
| } |
| } |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void managerDoubleNestedFetchGroupWithJoinFetch() { |
| managerNestedFetchGroupWithJoinFetch(true); |
| } |
| |
| @Test |
| public void managerTripleNestedFetchGroupWithJoinFetch() { |
| managerNestedFetchGroupWithJoinFetch(false); |
| } |
| |
| void managerNestedFetchGroupWithJoinFetch(boolean isDouble) { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.manager.manager IS NOT NULL"); |
| FetchGroup managerFG = new FetchGroup(); |
| if(isDouble) { |
| // Double |
| managerFG.addAttribute("manager.manager"); |
| } else { |
| // Triple |
| managerFG.addAttribute("manager.manager.manager"); |
| } |
| |
| query.setHint(QueryHints.FETCH_GROUP, managerFG); |
| query.setHint(QueryHints.LEFT_FETCH, "e.manager"); |
| |
| assertNotNull(getFetchGroup(query)); |
| assertSame(managerFG, getFetchGroup(query)); |
| |
| List<Employee> employees = query.getResultList(); |
| |
| int nSql; |
| if(isDouble) { |
| // In this case the number of generated sqls is unpredictable. |
| // Additional sql generated for every object that |
| // has been first fetched as manager.manager |
| // and then is selected as an employee - getting its manger |
| // performed without fetch group therefore triggering reading of the whole object |
| nSql = getQuerySQLTracker(em).getTotalSQLSELECTCalls(); |
| } else { |
| assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| nSql = 1; |
| } |
| |
| Employee emp = employees.get(0); |
| assertFetched(emp, managerFG); |
| |
| // manager (if not null) is instantiated by the fetch group, before emp.getManager call. |
| Employee manager = emp.getManager(); |
| assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| assertFetched(manager, managerFG); |
| |
| // instantiates the whole object |
| emp.getLastName(); |
| nSql++; |
| assertNoFetchGroup(emp); |
| assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| |
| assertFetched(manager, managerFG); |
| // instantiates the whole object |
| manager.getLastName(); |
| nSql++; |
| assertNoFetchGroup(manager); |
| assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| |
| nSql++; |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| assertFetched(phone, defaultPhoneFG); |
| phone.getAreaCode(); |
| nSql++; |
| assertNoFetchGroup(phone); |
| } |
| assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| |
| nSql++; |
| for (PhoneNumber phone : manager.getPhoneNumbers()) { |
| assertFetched(phone, defaultPhoneFG); |
| phone.getAreaCode(); |
| nSql++; |
| assertNoFetchGroup(phone); |
| } |
| assertEquals(nSql, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void allNestedFetchGroupWithJoinFetch() { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| // select employees who are neither managers nor team leaders |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE NOT EXISTS(SELECT p.id FROM Project p WHERE p.teamLeader = e) AND NOT EXISTS(SELECT e2.id FROM Employee e2 WHERE e2.manager = e)"); |
| FetchGroup employeeFG = new FetchGroup("employee"); |
| employeeFG.addAttribute("lastName"); |
| |
| employeeFG.addAttribute("address.country"); |
| employeeFG.addAttribute("address.city"); |
| query.setHint(QueryHints.LEFT_FETCH, "e.address"); |
| |
| employeeFG.addAttribute("phoneNumbers"); |
| query.setHint(QueryHints.LEFT_FETCH, "e.phoneNumbers"); |
| |
| employeeFG.addAttribute("projects.name"); |
| |
| employeeFG.addAttribute("projects.teamLeader.firstName"); |
| //employeeFG.addAttribute("projects.teamLeader.address.street"); |
| //employeeFG.addAttribute("projects.teamLeader.address.postalCode"); |
| employeeFG.addAttribute("projects.teamLeader.phoneNumbers.owner"); |
| employeeFG.addAttribute("projects.teamLeader.phoneNumbers.type"); |
| employeeFG.addAttribute("projects.teamLeader.phoneNumbers.areaCode"); |
| query.setHint(QueryHints.LEFT_FETCH, "e.projects.teamLeader.phoneNumbers"); |
| |
| employeeFG.addAttribute("manager.firstName"); |
| //employeeFG.addAttribute("manager.address.street"); |
| //employeeFG.addAttribute("manager.address.postalCode"); |
| employeeFG.addAttribute("manager.phoneNumbers.owner"); |
| employeeFG.addAttribute("manager.phoneNumbers.type"); |
| employeeFG.addAttribute("manager.phoneNumbers.areaCode"); |
| query.setHint(QueryHints.LEFT_FETCH, "e.manager.phoneNumbers"); |
| |
| // department attribute defined with JoinFetchType.OUTER |
| employeeFG.addAttribute("department.name"); |
| |
| query.setHint(QueryHints.FETCH_GROUP, employeeFG); |
| |
| List<Employee> employees = query.getResultList(); |
| assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| |
| for(Employee emp :employees) { |
| assertFetched(emp, employeeFG); |
| |
| Address address = emp.getAddress(); |
| if(address != null) { |
| assertFetched(address, employeeFG.getGroup("address")); |
| } |
| |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| assertFetched(phone, defaultPhoneFG); |
| } |
| |
| for (Project project : emp.getProjects()) { |
| assertFetched(project, employeeFG.getGroup("projects")); |
| Employee teamLeader = project.getTeamLeader(); |
| if(teamLeader != null) { |
| assertFetched(teamLeader, employeeFG.getGroup("projects.teamLeader")); |
| for (PhoneNumber phone : teamLeader.getPhoneNumbers()) { |
| assertFetched(phone, employeeFG.getGroup("projects.teamLeader.phoneNumbers")); |
| } |
| } |
| } |
| |
| Employee manager = emp.getManager(); |
| if(manager != null) { |
| assertFetched(manager, employeeFG.getGroup("manager")); |
| for (PhoneNumber phone : manager.getPhoneNumbers()) { |
| assertFetched(phone, employeeFG.getGroup("manager.phoneNumbers")); |
| } |
| } |
| |
| Department department = emp.getDepartment(); |
| if(department != null) { |
| assertFetched(department, employeeFG.getGroup("department")); |
| } |
| } |
| assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void joinFetchDefaultFetchGroup() throws Exception { |
| EntityManager em = createEntityManager(); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e"); |
| query.setHint(QueryHints.LEFT_FETCH, "e.phoneNumbers"); |
| |
| List<Employee> employees = query.getResultList(); |
| |
| for(Employee emp : employees) { |
| assertNoFetchGroup(emp); |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| assertFetched(phone, defaultPhoneFG); |
| } |
| } |
| } |
| |
| @Test |
| public void joinFetchOutsideOfFetchGroup() throws Exception { |
| EntityManager em = createEntityManager(); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e"); |
| // Define the fields to be fetched on Employee |
| FetchGroup fg = new FetchGroup(); |
| fg.addAttribute("firstName"); |
| fg.addAttribute("lastName"); |
| query.setHint(QueryHints.FETCH_GROUP, fg); |
| query.setHint(QueryHints.LEFT_FETCH, "e.address"); |
| |
| List<Employee> employees = query.getResultList(); |
| } |
| |
| @Test |
| public void simpleNestedFetchGroupWithBatch() { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e"); |
| |
| // Define the fields to be fetched on Employee |
| FetchGroup employeeFG = new FetchGroup(); |
| employeeFG.setShouldLoad(true); |
| employeeFG.addAttribute("firstName"); |
| employeeFG.addAttribute("lastName"); |
| employeeFG.addAttribute("address.country"); |
| employeeFG.addAttribute("address.city"); |
| |
| FetchGroup phonesFG = defaultPhoneFG.clone(); |
| // to preclude PhoneNumber from triggering owner's full read |
| phonesFG.addAttribute("owner.id"); |
| employeeFG.addAttribute("phoneNumbers", phonesFG); |
| |
| FetchGroup projectsFG = new FetchGroup("projects"); |
| projectsFG.addAttribute("name"); |
| projectsFG.addAttribute("name"); |
| // to preclude Project from triggering full read of the referenced Employee(s) |
| projectsFG.addAttribute("teamMembers.id"); |
| projectsFG.addAttribute("teamLeader.id"); |
| employeeFG.addAttribute("projects", projectsFG); |
| |
| query.setHint(QueryHints.FETCH_GROUP, employeeFG); |
| |
| query.setHint(QueryHints.BATCH, "e.address"); |
| query.setHint(QueryHints.BATCH, "e.phoneNumbers"); |
| query.setHint(QueryHints.BATCH, "e.projects"); |
| |
| // A single sql will be used to read all Project subclasses. |
| query.setHint(QueryHints.INHERITANCE_OUTER_JOIN, "true"); |
| |
| List<Employee> employees = query.getResultList(); |
| |
| // Employee, Address, PhoneNumbers, Projects - an sql per class. |
| // Address, PhoneNumbers and Projects are already loaded because |
| // employeeFG.shouldLoad is set to true. |
| assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls()); |
| |
| // verify fetch groups |
| for(Employee emp : employees) { |
| assertFetched(emp, employeeFG); |
| |
| Address address = emp.getAddress(); |
| if(address != null) { |
| assertFetched(address, employeeFG.getGroup("address")); |
| } |
| |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| assertFetched(phone, phonesFG); |
| } |
| |
| for (Project project : emp.getProjects()) { |
| assertFetched(project, projectsFG); |
| } |
| } |
| |
| // Now let's access an attribute outside of the fetch group. |
| // That triggers loading of the whole object. |
| for(Employee emp : employees) { |
| emp.getSalary(); |
| assertNoFetchGroup(emp); |
| |
| Address address = emp.getAddress(); |
| if(address != null) { |
| address.getStreet(); |
| assertNoFetchGroup(address); |
| } |
| |
| for (PhoneNumber phone : emp.getPhoneNumbers()) { |
| phone.getAreaCode(); |
| assertNoFetchGroup(phone); |
| } |
| |
| for (Project project : emp.getProjects()) { |
| project.getDescription(); |
| assertNoFetchGroup(project); |
| } |
| } |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| public void simpleLoadGroup() { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); |
| query.setParameter("GENDER", Gender.Female); |
| List<Employee> employees = query.getResultList(); |
| |
| LoadGroup group = new LoadGroup(); |
| group.addAttribute("address"); |
| group.addAttribute("phoneNumbers"); |
| group.addAttribute("manager.projects"); |
| AbstractSession session = (AbstractSession)((EntityManagerImpl)em.getDelegate()).getActiveSession(); |
| session.load(employees, group, session.getClassDescriptor(Employee.class), false); |
| |
| int numSelectBefore = getQuerySQLTracker(em).getTotalSQLSELECTCalls(); |
| |
| // All indirections specified in the plan should have been already triggered. |
| for(Employee emp : employees) { |
| emp.getAddress(); |
| emp.getPhoneNumbers().size(); |
| if(emp.getManager() != null) { |
| emp.getManager().getProjects().size(); |
| } |
| } |
| |
| int numSelectAfter = getQuerySQLTracker(em).getTotalSQLSELECTCalls(); |
| assertEquals(numSelectBefore, numSelectAfter); |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| |
| @Test |
| // Bug 325684 - QueryHints.BATCH combined with QueryHints.FETCH_GROUP_LOAD will cause NPE |
| public void simpleFetchGroupLoadWithBatch() { |
| EntityManager em = createEntityManager(); |
| try { |
| beginTransaction(em); |
| |
| FetchGroup projectGroup = new FetchGroup(); |
| projectGroup.addAttribute("name"); |
| |
| FetchGroup employeeGroup = new FetchGroup(); |
| employeeGroup.addAttribute("firstName"); |
| employeeGroup.addAttribute("lastName"); |
| employeeGroup.addAttribute("projects", projectGroup); |
| |
| Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); |
| query.setParameter("GENDER", Gender.Female); |
| query.setHint(QueryHints.FETCH_GROUP, employeeGroup); |
| query.setHint(QueryHints.FETCH_GROUP_LOAD, "true"); |
| query.setHint(QueryHints.BATCH, "e.projects"); |
| List<Employee> employees = query.getResultList(); |
| |
| int numSelectBefore = getQuerySQLTracker(em).getTotalSQLSELECTCalls(); |
| |
| // All indirections specified in the plan should have been already triggered. |
| for (Employee e : employees) { |
| e.getProjects().size(); |
| } |
| |
| int numSelectAfter = getQuerySQLTracker(em).getTotalSQLSELECTCalls(); |
| assertEquals(numSelectBefore, numSelectAfter); |
| } finally { |
| if (isTransactionActive(em)){ |
| rollbackTransaction(em); |
| } |
| closeEntityManager(em); |
| } |
| } |
| } |