FetchPlan extension Incubator - Bug 288307
- Added support for replacing QueryBasedValueHolder with BatchInConfig
- All 122 tests passing
- updated jar included
diff --git a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/eclipselink-incubator-fetchplan.jar b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/eclipselink-incubator-fetchplan.jar
index f2e10bc..a9a45a9 100644
--- a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/eclipselink-incubator-fetchplan.jar
+++ b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/eclipselink-incubator-fetchplan.jar
Binary files differ
diff --git a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/example.jpa.employee.xml/model/Address.java b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/example.jpa.employee.xml/model/Address.java
index 5cd52db..4b99ac5 100644
--- a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/example.jpa.employee.xml/model/Address.java
+++ b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/example.jpa.employee.xml/model/Address.java
@@ -74,4 +74,8 @@
public void setStreet(String street) {
this.street = street;
}
+
+ public String toString() {
+ return "Address(" + getId() + ")";
+ }
}
diff --git a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/example/FetchPlanExamples.java b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/example/FetchPlanExamples.java
index 4a025ff..c16bb83 100644
--- a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/example/FetchPlanExamples.java
+++ b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/example/FetchPlanExamples.java
@@ -42,14 +42,15 @@
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;
@@ -191,15 +192,15 @@
*/
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);
}
@@ -279,9 +280,7 @@
fetchPlan.addAttribute("address");
fetchPlan.addAttribute("phoneNumbers");
- int minId = ((Number) em.createQuery("SELECT MIN(e.id) FROM Employee e").getSingleResult()).intValue();
-
- Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = " + minId);
+ 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();
@@ -291,6 +290,11 @@
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);
}
diff --git a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/org/eclipse/persistence/extension/query/BatchInConfig.java b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/org/eclipse/persistence/extension/query/BatchInConfig.java
index b12de37..f0418b1 100644
--- a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/org/eclipse/persistence/extension/query/BatchInConfig.java
+++ b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/src/org/eclipse/persistence/extension/query/BatchInConfig.java
@@ -23,6 +23,7 @@
import org.eclipse.persistence.indirection.IndirectContainer;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.indirection.BatchValueHolder;
+import org.eclipse.persistence.internal.indirection.QueryBasedValueHolder;
import org.eclipse.persistence.internal.indirection.UnitOfWorkValueHolder;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jpa.JpaHelper;
@@ -33,7 +34,6 @@
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReadQuery;
-import org.eclipse.persistence.sessions.server.Server;
/**
* This is an extension to EclipseLink intending to assist customers who need to
@@ -84,7 +84,7 @@
return;
}
- Server session = JpaHelper.getEntityManager(em).getServerSession();
+ AbstractSession session = JpaHelper.getEntityManager(em).getServerSession();
ClassDescriptor descriptor = session.getClassDescriptor(results.get(0));
if (descriptor == null) {
@@ -105,7 +105,7 @@
throw new IllegalArgumentException("Invalid mapping type found: " + mapping);
}
- BatchValueHolder batchVH = getBatchValueHolder(frMapping, results);
+ BatchValueHolder batchVH = getBatchValueHolder(frMapping, results, (AbstractSession) session);
if (batchVH == null) {
SessionLog log = JpaHelper.getEntityManager(em).getServerSession().getSessionLog();
@@ -123,7 +123,7 @@
/**
* Find the first BatchValueHolder in the results
*/
- private static BatchValueHolder getBatchValueHolder(ForeignReferenceMapping mapping, List<?> results) {
+ private static BatchValueHolder getBatchValueHolder(ForeignReferenceMapping mapping, List<?> results, AbstractSession session) {
Object result = results.get(0);
Object value = mapping.getAttributeValueFromObject(result);
@@ -140,14 +140,18 @@
return (BatchValueHolder) value;
}
- // No BatchValueHolder found? TODO: Build one?
- return null;
+ if (value instanceof QueryBasedValueHolder) {
+ replaceQueryBasedValueHolders(mapping, results, session);
+ return getBatchValueHolder(mapping, results, session);
+ }
+
+ throw new IllegalStateException("TODO: How can we get here?");
}
/**
* Customize the query for a 1:1 or M:1
*/
- private static void configBatchInQuery(Server session, OneToOneMapping mapping, ReadAllQuery raq, List<?> results) {
+ private static void configBatchInQuery(AbstractSession session, OneToOneMapping mapping, ReadAllQuery raq, List<?> results) {
if (mapping.getForeignKeyFields().size() != 1) {
throw new IllegalArgumentException("Cannot configure batch using IN with composite FK: " + mapping);
}
@@ -159,7 +163,7 @@
if (vhi instanceof UnitOfWorkValueHolder) {
vhi = ((UnitOfWorkValueHolder) vhi).getWrappedValueHolder();
}
- BatchValueHolder bvh = (BatchValueHolder) vhi;
+ QueryBasedValueHolder bvh = (QueryBasedValueHolder) vhi;
ids[index] = bvh.getRow().get(mapping.getForeignKeyFields().get(0));
}
@@ -173,12 +177,12 @@
/**
* Customize the query for a 1:M
*/
- private static void configBatchInQuery(Server session, OneToManyMapping mapping, ReadAllQuery raq, List<?> results) {
+ private static void configBatchInQuery(AbstractSession session, OneToManyMapping mapping, ReadAllQuery raq, List<?> results) {
if (raq == null) {
// TODO: This occurs when there is no query available.
return;
}
-
+
if (mapping.getTargetForeignKeyFields().size() != 1) {
throw new IllegalArgumentException("Cannot configure batch using IN with composite FK: " + mapping);
}
@@ -224,9 +228,32 @@
ReadQuery batchQuery = frMapping.prepareNestedBatchQuery(raq);
Object value = frMapping.getIndirectionPolicy().valueFromBatchQuery(batchQuery, null, raq);
-
- for (Object result: results) {
+
+ for (Object result : results) {
frMapping.setAttributeValueInObject(result, value);
}
}
+
+ /**
+ * Replace All existing {@link QueryBasedValueHolder} in a set of entities
+ * for the provided mapping with {@link BatchValueHolder} that are later
+ * updated to use the IN operation.
+ */
+ private static void replaceQueryBasedValueHolders(ForeignReferenceMapping mapping, List<?> results, AbstractSession session) {
+ // Create fake source query
+ ReadAllQuery sourceQuery = new ReadAllQuery(mapping.getDescriptor().getJavaClass());
+ sourceQuery.setSession(session);
+ sourceQuery.setDescriptor(mapping.getDescriptor());
+
+ ReadQuery batchQuery = mapping.prepareNestedBatchQuery(sourceQuery);
+
+ for (Object obj : results) {
+ ValueHolderInterface vhi = (ValueHolderInterface) mapping.getAttributeValueFromObject(obj);
+ if (vhi instanceof UnitOfWorkValueHolder) {
+ vhi = ((UnitOfWorkValueHolder) vhi).getWrappedValueHolder();
+ }
+ Object batchVH = mapping.getIndirectionPolicy().valueFromBatchQuery(batchQuery, ((QueryBasedValueHolder) vhi).getRow(), sourceQuery);
+ mapping.setAttributeValueInObject(obj, batchVH);
+ }
+ }
}
diff --git a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/FetchPlanFetchWithBatchInTests.java b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/FetchPlanFetchWithBatchInTests.java
index 9d27a69..cee17f7 100644
--- a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/FetchPlanFetchWithBatchInTests.java
+++ b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/FetchPlanFetchWithBatchInTests.java
@@ -12,7 +12,7 @@
******************************************************************************/
package test;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashSet;
@@ -136,7 +136,7 @@
public void findEmployeesWithBatchInConfigFetch() {
EntityManager em = getEntityManager();
- Query q = em.createQuery("SELECT em FROM Employee em");
+ Query q = em.createQuery("SELECT em FROM Employee em ORDER BY em.id");
q.setHint(QueryHints.BATCH, "em.managedEmployees");
q.setHint(QueryHints.BATCH, "em.managedEmployees.address");
@@ -153,7 +153,7 @@
fp.addAttribute("managedEmployees");
JpaFetchPlanHelper.fetch(em, fp, ems);
- // interate through all employees and collect their managers
+ // iterate through all employees and collect their managers
Set<Employee> managedEmployees = new HashSet<Employee>();
for (Employee employee : ems) {
managedEmployees.addAll(employee.getManagedEmployees());
@@ -165,6 +165,120 @@
fp2.addAttribute("address");
JpaFetchPlanHelper.fetch(em, fp2, managedEmployees);
+ assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+
+ System.out.println("Employees Read - " + ems.size());
+ for (Employee emp : ems) {
+ System.out.println("\t> " + emp);
+ for (Employee managedEmp : emp.getManagedEmployees()) {
+ System.out.println("\t\t> " + managedEmp);
+ System.out.println("\t\t\t> " + managedEmp.getAddress());
+ }
+ if (emp.getManagedEmployees() == null) {
+
+ }
+ }
+
+ assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+
+ em.clear();
+ JpaHelper.getServerSession(getEMF()).getIdentityMapAccessor().initializeAllIdentityMaps();
+
+ q = em.createNativeQuery("SELECT EMP_ID, ADDR_ID, MANAGER_ID FROM EMPLOYEE ORDER BY EMP_ID");
+ List<Object[]> ids = q.getResultList();
+
+ assertEquals(4, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+ assertEquals(ems.size(), ids.size());
+
+ for (int index = 0; index < ems.size(); index++) {
+ Employee emp = ems.get(index);
+ Object[] idRow = ids.get(index);
+
+ assertEquals(emp.getId(), ((Number) idRow[0]).intValue());
+ if (idRow[2] != null) { // We have a manager
+ assertNotNull(emp.getAddress());
+ assertEquals(emp.getAddress().getId(), ((Number) idRow[1]).intValue());
+ } else {
+
+ }
+ }
+ }
+
+ /**
+ * This test does not used BatchIn but is here to provide a verification for
+ * the assertions of {@link #findEmployeesWithBatchInConfigFetch()}
+ */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void findEmployeesWithJoinBatching() {
+ EntityManager em = getEntityManager();
+
+ Query q = em.createQuery("SELECT em FROM Employee em ORDER BY em.id");
+ q.setHint(QueryHints.BATCH, "em.managedEmployees");
+ q.setHint(QueryHints.BATCH, "em.managedEmployees.address");
+
+ List<Employee> ems = q.getResultList();
+
+ // Display initial Employees read:
+ System.out.println("Employees Read - " + ems.size());
+ for (Employee emp : ems) {
+ System.out.println("\t> " + emp);
+ }
+
+ //BatchInConfig.config(em, ems, "managedEmployees");
+ FetchPlan fp = new FetchPlan(Employee.class);
+ fp.addAttribute("managedEmployees");
+ JpaFetchPlanHelper.fetch(em, fp, ems);
+
+ // iterate through all employees and collect their managers
+ Set<Employee> managedEmployees = new HashSet<Employee>();
+ for (Employee employee : ems) {
+ managedEmployees.addAll(employee.getManagedEmployees());
+ }
+
+ // Fetch for all managers the addresses
+ //BatchInConfig.config(em, new ArrayList<Employee>(managedEmployees), "address");
+ FetchPlan fp2 = new FetchPlan(Employee.class);
+ fp2.addAttribute("address");
+ JpaFetchPlanHelper.fetch(em, fp2, managedEmployees);
+
+ assertEquals(13, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+
+ System.out.println("Employees Read - " + ems.size());
+ for (Employee emp : ems) {
+ System.out.println("\t> " + emp);
+ for (Employee managedEmp : emp.getManagedEmployees()) {
+ System.out.println("\t\t> " + managedEmp);
+ System.out.println("\t\t\t> " + managedEmp.getAddress());
+ }
+ if (emp.getManagedEmployees() == null) {
+
+ }
+ }
+
+ assertEquals(13, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+
+ em.clear();
+ JpaHelper.getServerSession(getEMF()).getIdentityMapAccessor().initializeAllIdentityMaps();
+
+ q = em.createNativeQuery("SELECT e.EMP_ID, e.ADDR_ID, e.MANAGER_ID FROM EMPLOYEE e, SALARY s WHERE s.EMP_ID = e.EMP_ID ORDER BY e.EMP_ID");
+ List<Object[]> ids = q.getResultList();
+
+ assertEquals(14, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+ assertEquals(ems.size(), ids.size());
+
+ for (int index = 0; index < ems.size(); index++) {
+ Employee emp = ems.get(index);
+ Object[] idRow = ids.get(index);
+
+ assertEquals(emp.getId(), ((Number) idRow[0]).intValue());
+ if (idRow[2] != null) { // We have a manager
+ assertNotNull(emp.getAddress());
+ assertEquals("Incorrect address on: " + emp, emp.getAddress().getId(), ((Number) idRow[1]).intValue());
+ } else {
+
+ }
+ }
}
/**
diff --git a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/fetchplan/FetchPlanExamplesTests.java b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/fetchplan/FetchPlanExamplesTests.java
index 5985d63..b009635 100644
--- a/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/fetchplan/FetchPlanExamplesTests.java
+++ b/extensions/trunk/org.eclipse.persistence.example.jpa.fetchplan/test-src/test/fetchplan/FetchPlanExamplesTests.java
@@ -323,6 +323,54 @@
}
@Test
+ public void copyMergeExample_Clear() throws Exception {
+ EntityManager em = getEntityManager();
+
+ em.getTransaction().begin();
+ this.examples.copyMergeExample(em, true);
+
+ assertEquals(6, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLINSERTCalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLUPDATECalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLDELETECalls());
+
+ try {
+ em.flush();
+
+ assertEquals(6, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLINSERTCalls());
+ assertEquals(1, getQuerySQLTracker(em).getTotalSQLUPDATECalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLDELETECalls());
+ } finally {
+ em.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void copyMergeExample_NoClear() throws Exception {
+ EntityManager em = getEntityManager();
+
+ em.getTransaction().begin();
+ this.examples.copyMergeExample(em, false);
+
+ assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLINSERTCalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLUPDATECalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLDELETECalls());
+
+ try {
+ em.flush();
+
+ assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLINSERTCalls());
+ assertEquals(1, getQuerySQLTracker(em).getTotalSQLUPDATECalls());
+ assertEquals(0, getQuerySQLTracker(em).getTotalSQLDELETECalls());
+ } finally {
+ em.getTransaction().rollback();
+ }
+ }
+
+ @Test
public void fetchCopyMergeExample() throws Exception {
EntityManager em = getEntityManager();