/******************************************************************************* | |
* Copyright (c) 1998, 2009 Oracle. 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: | |
* dclarke - Bug 288307: Extensions Incubator - FetchPlan | |
******************************************************************************/ | |
package example; | |
import java.util.List; | |
import javax.persistence.EntityManager; | |
import javax.persistence.Query; | |
import model.Employee; | |
import model.Gender; | |
import org.eclipse.persistence.config.QueryHints; | |
import org.eclipse.persistence.extension.fetchplan.FetchPlan; | |
import org.eclipse.persistence.extension.fetchplan.JpaFetchPlanHelper; | |
import org.eclipse.persistence.jpa.JpaHelper; | |
@SuppressWarnings("unchecked") | |
public class FetchPlanExamples { | |
/** | |
* Query for all employees with a salary greater then zero using a | |
* FetchGroup to only load id, firstName, lastName, gender, salary, and | |
* version and then fetch their address and phone numbers using the same | |
* FetchPlan. | |
*/ | |
public List<Employee> employeesFetchAddressAndPhones(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("gender"); | |
fetchPlan.addAttribute("salary"); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.salary > 0"); | |
// Configure a dynamic FetchGroup based on the FetchPlan | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
List<Employee> emps = query.getResultList(); | |
// Force the fetch operation on the query result | |
JpaFetchPlanHelper.fetch(em, fetchPlan, emps); | |
return emps; | |
} | |
/** | |
* Similar to {@link #employeesFetchAddressAndPhones(EntityManager)} but the | |
* loading of the related address and phoneNumbers is optimized using batch | |
* reading. | |
*/ | |
// Note: FetchGroup with FETCH (JOIN) does not work - see bug XXX | |
public List<Employee> employeesFetchAddressAndPhones_optimized(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("gender"); | |
fetchPlan.addAttribute("salary"); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.salary > 0"); | |
// Configure a dynamic FetchGroup based on the FetchPlan | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
query.setHint(QueryHints.BATCH, "e.address"); | |
query.setHint(QueryHints.BATCH, "e.phoneNumbers"); | |
List<Employee> emps = query.getResultList(); | |
JpaFetchPlanHelper.fetch(em, fetchPlan, emps); | |
return emps; | |
} | |
public List<Employee> employeesFetchAddressAndPhonesBatchAndUsingRedirector(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("gender"); | |
fetchPlan.addAttribute("salary"); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.salary > 0"); | |
// Configure a dynamic FetchGroup based on the FetchPlan | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
query.setHint(QueryHints.BATCH, "e.address"); | |
query.setHint(QueryHints.BATCH, "e.phoneNumbers"); | |
// Configure redirector so the FetchPlan.fetch is executed automatically | |
fetchPlan.fetchOnExecute(JpaHelper.getReadAllQuery(query)); | |
return query.getResultList(); | |
} | |
/** | |
* Simple example retrieving just the firstName and lastName of the | |
* Employee. This will also include the required identifier and optimistic | |
* locking values (id and version). | |
*/ | |
public List<Employee> maleEmployeeCopyNames(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); | |
query.setParameter("GENDER", Gender.Male); | |
// Configure a dynamic FetchGroup based on the FetchPlan | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
List<Employee> emps = query.getResultList(); | |
// This ensures all required relationships are loaded | |
// In this case it does nothing | |
JpaFetchPlanHelper.fetch(em, fetchPlan, emps); | |
// Get a set of copies with only the names and required attributes | |
// populated | |
List<Employee> copies = JpaFetchPlanHelper.copy(em, fetchPlan, emps); | |
return copies; | |
} | |
/** | |
* This example illustrates how a FetchPlan can be constructed based on the | |
* default fetch configuration of the mappings and used to copy the | |
* resulting entities. This can be used to create a detached entity graph | |
* where all EAGER attributes are loaded and all LAZY attributes are | |
* returned as NULL. | |
*/ | |
public List<Employee> copyEagerAttributesOnly(EntityManager em) { | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.firstName IS NOT NULL AND e.lastName IS NOT NULL AND e.salary > 0 "); | |
// The result of this query is a collection of Employee instances where | |
// all EAGER attributes are populated and all LAZY attributes are | |
// available | |
// for loading with proxy objects (FetchGroup for basics, ValueHolders | |
// for 1:1 and M:1, and IndirectList/Map/Set for collections). If | |
// serialized at this point these will not appear as null. | |
List<Employee> emps = query.getResultList(); | |
// Create a FetchPlan based on the default FetchGroup | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
JpaFetchPlanHelper.addDefaultFetchAttributes(em, fetchPlan); | |
// Get a set of copies with only the names and required attributes | |
// populated | |
List<Employee> copies = JpaFetchPlanHelper.copy(em, fetchPlan, emps); | |
return copies; | |
} | |
/** | |
* Load all employees's first and last names (plus required id and version) | |
* attributes based on a FetchGroup and copy the results based on this plan. | |
*/ | |
public List<Employee> employeeCopyNamesWithFetchGroup(EntityManager em) { | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender IS NOT NULL"); | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
// Configure a dynamic FetchGroup based on the FetchPlan | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
List<Employee> emps = query.getResultList(); | |
return JpaFetchPlanHelper.copy(em, fetchPlan, emps); | |
} | |
/** | |
* Create copies of managed objects requiring relationships that were not | |
* loaded in the initial query. The copy operation will force the fetching | |
* of required relationships. | |
*/ | |
public List<Employee> employeeCopyWithNamesAddressAndPhones(EntityManager em) { | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.salary > 0"); | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
List<Employee> emps = query.getResultList(); | |
return JpaFetchPlanHelper.copy(em, fetchPlan, emps); | |
} | |
/** | |
* Create copies of managed objects requiring relationships that were not | |
* loaded in the initial query. The copy operation will force the fetching | |
* of required relationships. | |
*/ | |
public List<Employee> employeeCopyWithNamesAddressAndPhonesWithBatching(EntityManager em) { | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.salary > 0"); | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
// Optimize graph loading using batching | |
query.setHint(QueryHints.BATCH, "e.address"); | |
query.setHint(QueryHints.BATCH, "e.phoneNumbers"); | |
List<Employee> emps = query.getResultList(); | |
return JpaFetchPlanHelper.copy(em, fetchPlan, emps); | |
} | |
/** | |
* Illustrate a multi-level fetch | |
*/ | |
public List<Employee> managerManagerManagerFetchWithNames(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("manager.firstName"); | |
fetchPlan.addAttribute("manager.lastName"); | |
fetchPlan.addAttribute("manager.manager.firstName"); | |
fetchPlan.addAttribute("manager.manager.lastName"); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.manager.manager IS NOT NULL"); | |
List<Employee> emps = query.getResultList(); | |
JpaFetchPlanHelper.fetch(em, fetchPlan, emps); | |
return emps; | |
} | |
/** | |
* Example of a composite select returning both an Employee and its count of | |
* PhoneNumbers. To use the {@link FetchPlan} in this case you must also | |
* provide the index into the resulting Object[]. | |
*/ | |
public List<Object[]> employeeAddress_ReturnBoth(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
Query query = em.createQuery("SELECT e, e.address FROM Employee e WHERE e.firstName <> e.lastName"); | |
List<Object[]> results = query.getResultList(); | |
JpaFetchPlanHelper.fetch(em, fetchPlan, results, 0); | |
return results; | |
} | |
/** | |
* Example of using a FetchPlan to retrieve an entity and copy a partial | |
* unmanaged graph of the entity which is then modified and merged back into | |
* the persistence context. | |
* | |
* Note: caller manages transactions | |
*/ | |
public void copyMergeExample(EntityManager em, boolean clear) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
fetchPlan.addAttribute("firstName"); | |
fetchPlan.addAttribute("lastName"); | |
fetchPlan.addAttribute("address"); | |
fetchPlan.addAttribute("phoneNumbers"); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = (SELECT MIN(ee.id) FROM Employee ee)"); | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
Employee emp = (Employee) query.getSingleResult(); | |
Employee copy = JpaFetchPlanHelper.copy(em, fetchPlan, emp); | |
copy.setSalary(Integer.MAX_VALUE); | |
copy.setFirstName(emp.getLastName()); | |
copy.setLastName(emp.getFirstName()); | |
if (clear) { | |
em.clear(); | |
JpaHelper.getEntityManager(em).getServerSession().getIdentityMapAccessor().initializeAllIdentityMaps(); | |
} | |
JpaFetchPlanHelper.merge(em, fetchPlan, copy); | |
} | |
/** | |
* | |
*/ | |
public List<Employee> createFetchPlanFromDefaultFetchGroup(EntityManager em) { | |
FetchPlan fetchPlan = new FetchPlan(Employee.class); | |
JpaFetchPlanHelper.addDefaultFetchAttributes(em, fetchPlan); | |
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.salary > 0"); | |
query.setHint(QueryHints.FETCH_GROUP, fetchPlan.createFetchGroup()); | |
List<Employee> emps = query.getResultList(); | |
JpaFetchPlanHelper.fetch(em, fetchPlan, emps); | |
return emps; | |
} | |
} |