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();