cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject svn commit: r620119 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/ main/java/org/apache/cayenne/query/ test/java/org/apache/cayenne/access/
Date Sat, 09 Feb 2008 14:10:26 GMT
Author: aadamchik
Date: Sat Feb  9 06:10:25 2008
New Revision: 620119

URL: http://svn.apache.org/viewvc?rev=620119&view=rev
Log:
CAY-839 Support combination of Persistent objects and scalars in query results
(backend processing of full SQLResultSetMapping)
(also fixes CAY-983 'POST_LOAD called twice' as a side effect of the algorithm change)

Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SQLResultSetMapping.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java?rev=620119&r1=620118&r2=620119&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
(original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
Sat Feb  9 06:10:25 2008
@@ -23,7 +23,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -41,6 +40,8 @@
 import org.apache.cayenne.map.LifecycleEvent;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.query.EntityResult;
+import org.apache.cayenne.query.FieldResult;
 import org.apache.cayenne.query.ObjectIdQuery;
 import org.apache.cayenne.query.PrefetchSelectQuery;
 import org.apache.cayenne.query.PrefetchTreeNode;
@@ -124,37 +125,12 @@
         }
 
         if (!noObjectConversion) {
-
-            if (interceptMappedConversion() != DONE) {
-                interceptObjectConversion();
-            }
-
-            invokePostLoad();
+            interceptObjectConversion();
         }
 
         return response;
     }
 
-    private void invokePostLoad() {
-        // TODO: andrus, 9/21/2006 - this method incorrectly calls "postLoad" when query
-        // refresh flag is set to false and object is already there.
-
-        LifecycleCallbackRegistry callbackRegistry = domain
-                .getEntityResolver()
-                .getCallbackRegistry();
-
-        if (!callbackRegistry.isEmpty(LifecycleEvent.POST_LOAD)) {
-
-            List list = response.firstList();
-            if (list != null
-                    && !list.isEmpty()
-                    && !(query.getMetaData(domain.getEntityResolver()))
-                            .isFetchingDataRows()) {
-                callbackRegistry.performCallbacks(LifecycleEvent.POST_LOAD, list);
-            }
-        }
-    }
-
     private boolean interceptDataDomainQuery() {
         if (query instanceof DataDomainQuery) {
             response = new ListResponse(domain);
@@ -280,7 +256,8 @@
                 return DONE;
             }
 
-            Collection<Persistent> objects = (Collection<Persistent>) refreshQuery.getObjects();
+            Collection<Persistent> objects = (Collection<Persistent>) refreshQuery
+                    .getObjects();
             if (objects != null && !objects.isEmpty()) {
 
                 Collection<ObjectId> ids = new ArrayList<ObjectId>(objects.size());
@@ -446,113 +423,32 @@
 
         if (context != null && !metadata.isFetchingDataRows()) {
 
-            List mainRows = response.firstList();
+            List<DataRow> mainRows = response.firstList();
             if (mainRows != null && !mainRows.isEmpty()) {
 
-                List objects;
-                ClassDescriptor descriptor = metadata.getClassDescriptor();
-                PrefetchTreeNode prefetchTree = metadata.getPrefetchTree();
-
-                // take a shortcut when no prefetches exist...
-                if (prefetchTree == null) {
-                    objects = new ObjectResolver(context, descriptor, metadata
-                            .isRefreshingObjects(), metadata.isResolvingInherited())
-                            .synchronizedObjectsFromDataRows(mainRows);
-                }
-                else {
-
-                    ObjectTreeResolver resolver = new ObjectTreeResolver(
-                            context,
-                            metadata);
-                    objects = resolver.synchronizedObjectsFromDataRows(
-                            prefetchTree,
-                            mainRows,
-                            prefetchResultsByPath);
-                }
+                ObjectConversionStrategy converter;
 
-                if (response instanceof GenericResponse) {
-                    ((GenericResponse) response).replaceResult(mainRows, objects);
-                }
-                else if (response instanceof ListResponse) {
-                    this.response = new ListResponse(objects);
+                SQLResultSetMapping rsMapping = metadata.getResultSetMapping();
+                if (rsMapping == null) {
+                    converter = new SingleObjectConversionStrategy();
+                }
+                else if (rsMapping.getEntityResults().size() == 1
+                        && rsMapping.getColumnResults().size() == 0) {
+                    converter = new SingleObjectConversionStrategy();
+                }
+                else if (rsMapping.getEntityResults().size() == 0
+                        && rsMapping.getColumnResults().size() == 1) {
+                    converter = new SingleScalarConversionStrategy();
                 }
                 else {
-                    throw new IllegalStateException("Unknown response object: "
-                            + this.response);
+                    converter = new MixedConversionStrategy();
                 }
 
-                // apply POST_LOAD callback
-                LifecycleCallbackRegistry callbackRegistry = context
-                        .getEntityResolver()
-                        .getCallbackRegistry();
-
-                if (!callbackRegistry.isEmpty(LifecycleEvent.POST_LOAD)) {
-                    callbackRegistry.performCallbacks(LifecycleEvent.POST_LOAD, objects);
-                }
+                converter.convert(mainRows);
             }
         }
     }
 
-    private boolean interceptMappedConversion() {
-        SQLResultSetMapping rsMapping = metadata.getResultSetMapping();
-
-        if (rsMapping == null) {
-            return !DONE;
-        }
-
-        List mainRows = response.firstList();
-        if (mainRows != null && !mainRows.isEmpty()) {
-
-            Collection<String> columns = rsMapping.getColumnResults();
-            if (columns.isEmpty()) {
-                throw new CayenneRuntimeException(
-                        "Invalid result set mapping, no columns mapped.");
-            }
-
-            Object[] columnsArray = columns.toArray();
-            int rowsLen = mainRows.size();
-            int rowWidth = columnsArray.length;
-
-            List objects = new ArrayList(rowsLen);
-
-            // add scalars to the result
-            if (rowWidth == 1) {
-
-                for (int i = 0; i < rowsLen; i++) {
-                    Map row = (Map) mainRows.get(i);
-                    objects.add(row.get(columnsArray[0]));
-                }
-            }
-            // add Object[]'s to the result
-            else {
-                for (int i = 0; i < rowsLen; i++) {
-                    Map row = (Map) mainRows.get(i);
-                    Object[] rowDecoded = new Object[rowWidth];
-
-                    for (int j = 0; j < rowWidth; j++) {
-                        rowDecoded[j] = row.get(columnsArray[j]);
-                    }
-
-                    objects.add(rowDecoded);
-                }
-            }
-
-            if (response instanceof GenericResponse) {
-                ((GenericResponse) response).replaceResult(mainRows, objects);
-            }
-            else if (response instanceof ListResponse) {
-                this.response = new ListResponse(objects);
-            }
-            else {
-                throw new IllegalStateException("Unknown response object: "
-                        + this.response);
-            }
-
-        }
-
-        return DONE;
-    }
-
     public void route(QueryEngine engine, Query query, Query substitutedQuery) {
 
         Collection<Query> queries = null;
@@ -642,5 +538,178 @@
 
     public boolean isIteratedResult() {
         return false;
+    }
+
+    abstract class ObjectConversionStrategy {
+
+        abstract void convert(List<DataRow> mainRows);
+
+        protected List<Persistent> toObjects(
+                ClassDescriptor descriptor,
+                PrefetchTreeNode prefetchTree,
+                List<DataRow> normalizedRows) {
+            List<Persistent> objects;
+
+            // take a shortcut when no prefetches exist...
+            if (prefetchTree == null) {
+                objects = new ObjectResolver(context, descriptor, metadata
+                        .isRefreshingObjects(), metadata.isResolvingInherited())
+                        .synchronizedObjectsFromDataRows(normalizedRows);
+            }
+            else {
+                ObjectTreeResolver resolver = new ObjectTreeResolver(context, metadata);
+                objects = resolver.synchronizedObjectsFromDataRows(
+                        prefetchTree,
+                        normalizedRows,
+                        prefetchResultsByPath);
+            }
+            return objects;
+        }
+
+        protected List<DataRow> toNormalizedDataRows(
+                EntityResult entityMapping,
+                List<DataRow> dataRows) {
+            List<DataRow> normalized = new ArrayList<DataRow>(dataRows.size());
+
+            FieldResult[] fields = entityMapping.getDbFields(domain.getEntityResolver());
+            int rowCapacity = (int) Math.ceil(fields.length / 0.75);
+
+            for (DataRow src : dataRows) {
+                DataRow target = new DataRow(rowCapacity);
+
+                for (FieldResult columnMapping : fields) {
+                    target.put(columnMapping.getAttributeName(), src.get(columnMapping
+                            .getColumn()));
+                }
+
+                normalized.add(target);
+            }
+
+            return normalized;
+        }
+
+        protected void updateResponse(List sourceObjects, List targetObjects) {
+            if (response instanceof GenericResponse) {
+                ((GenericResponse) response).replaceResult(sourceObjects, targetObjects);
+            }
+            else if (response instanceof ListResponse) {
+                response = new ListResponse(targetObjects);
+            }
+            else {
+                throw new IllegalStateException("Unknown response object: " + response);
+            }
+        }
+    }
+
+    class SingleObjectConversionStrategy extends ObjectConversionStrategy {
+
+        @Override
+        void convert(List<DataRow> mainRows) {
+
+            // convert data rows to standardized format...
+            SQLResultSetMapping rsMapping = metadata.getResultSetMapping();
+            if (rsMapping != null) {
+                // expect 1 and only 1 entityMapping...
+                EntityResult entityMapping = rsMapping.getEntityResults().get(0);
+                mainRows = toNormalizedDataRows(entityMapping, mainRows);
+            }
+
+            ClassDescriptor descriptor = metadata.getClassDescriptor();
+            PrefetchTreeNode prefetchTree = metadata.getPrefetchTree();
+
+            List<Persistent> objects = toObjects(descriptor, prefetchTree, mainRows);
+            updateResponse(mainRows, objects);
+
+            // apply POST_LOAD callback
+            LifecycleCallbackRegistry callbackRegistry = context
+                    .getEntityResolver()
+                    .getCallbackRegistry();
+
+            if (!callbackRegistry.isEmpty(LifecycleEvent.POST_LOAD)) {
+                callbackRegistry.performCallbacks(LifecycleEvent.POST_LOAD, objects);
+            }
+        }
+    }
+
+    class SingleScalarConversionStrategy extends ObjectConversionStrategy {
+
+        @Override
+        void convert(List<DataRow> mainRows) {
+
+            SQLResultSetMapping rsMapping = metadata.getResultSetMapping();
+
+            List<String> columns = rsMapping.getColumnResults();
+
+            int rowsLen = mainRows.size();
+
+            List objects = new ArrayList(rowsLen);
+            String column = columns.get(0);
+
+            // add scalars to the result
+            for (DataRow row : mainRows) {
+                objects.add(row.get(column));
+            }
+
+            updateResponse(mainRows, objects);
+        }
+    }
+
+    class MixedConversionStrategy extends ObjectConversionStrategy {
+
+        @Override
+        void convert(List<DataRow> mainRows) {
+
+            int rowsLen = mainRows.size();
+            List<Object[]> objects = new ArrayList<Object[]>(rowsLen);
+
+            SQLResultSetMapping rsMapping = metadata.getResultSetMapping();
+
+            List<EntityResult> entities = rsMapping.getEntityResults();
+            List<String> columns = rsMapping.getColumnResults();
+
+            // pass 1 - init Object[]'s and resolve scalars
+
+            int resultWidth = entities.size() + columns.size();
+            int scalarOffset = entities.size();
+
+            for (DataRow row : mainRows) {
+                Object[] resultRow = new Object[resultWidth];
+                for (int i = 0; i < columns.size(); i++) {
+                    resultRow[scalarOffset + i] = row.get(columns.get(i));
+                }
+                objects.add(resultRow);
+            }
+
+            // pass 2 - resolve individual object lists
+            List[] resultLists = new List[scalarOffset];
+            for (int i = 0; i < scalarOffset; i++) {
+                EntityResult entityMapping = entities.get(i);
+                List<DataRow> normalized = toNormalizedDataRows(entityMapping, mainRows);
+
+                List<Persistent> nextResult = toObjects(entityMapping
+                        .getClassDescriptor(domain.getEntityResolver()), null, normalized);
+
+                for (int j = 0; j < rowsLen; j++) {
+                    objects.get(j)[i] = nextResult.get(j);
+                }
+
+                resultLists[i] = nextResult;
+            }
+
+            updateResponse(mainRows, objects);
+
+            // invoke callbacks now that all objects are resolved...
+            LifecycleCallbackRegistry callbackRegistry = context
+                    .getEntityResolver()
+                    .getCallbackRegistry();
+
+            if (!callbackRegistry.isEmpty(LifecycleEvent.POST_LOAD)) {
+                for (int i = 0; i < scalarOffset; i++) {
+                    callbackRegistry.performCallbacks(
+                            LifecycleEvent.POST_LOAD,
+                            resultLists[i]);
+                }
+            }
+        }
     }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SQLResultSetMapping.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SQLResultSetMapping.java?rev=620119&r1=620118&r2=620119&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SQLResultSetMapping.java
(original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SQLResultSetMapping.java
Sat Feb  9 06:10:25 2008
@@ -35,6 +35,7 @@
 public class SQLResultSetMapping {
 
     protected String name;
+    protected List<EntityResult> entityResults;
     protected List<String> columnResults;
 
     public SQLResultSetMapping() {
@@ -51,6 +52,18 @@
 
     public void setName(String name) {
         this.name = name;
+    }
+
+    public List<EntityResult> getEntityResults() {
+        return entityResults != null ? entityResults : Collections.EMPTY_LIST;
+    }
+
+    public void addEntityResult(EntityResult entityResult) {
+        if (entityResults == null) {
+            entityResults = new ArrayList<EntityResult>(3);
+        }
+        
+        entityResults.add(entityResult);
     }
 
     /**

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java?rev=620119&r1=620118&r2=620119&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java
(original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java
Sat Feb  9 06:10:25 2008
@@ -34,6 +34,7 @@
 import org.apache.cayenne.dba.frontbase.FrontBaseAdapter;
 import org.apache.cayenne.dba.openbase.OpenBaseAdapter;
 import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.query.EntityResult;
 import org.apache.cayenne.query.SQLResultSetMapping;
 import org.apache.cayenne.query.SQLTemplate;
 import org.apache.cayenne.unit.CayenneCase;
@@ -50,6 +51,49 @@
         super.setUp();
         deleteTestData();
         context = createDataContext();
+    }
+
+    public void testSQLResultSetMappingMixed() throws Exception {
+        createTestData("prepare");
+
+        String sql = "SELECT t0.ARTIST_ID AS X, t0.ARTIST_NAME AS Y, t0.DATE_OF_BIRTH AS
Z, count(t1.PAINTING_ID) AS C "
+                + "FROM ARTIST t0 LEFT JOIN PAINTING t1 ON (t0.ARTIST_ID = t1.ARTIST_ID)
"
+                + "GROUP BY t0.ARTIST_ID, t0.ARTIST_NAME, t0.DATE_OF_BIRTH "
+                + "ORDER BY t0.ARTIST_ID";
+
+        DataMap map = getDomain().getMap("testmap");
+        SQLTemplate query = new SQLTemplate(map, sql);
+        query.setColumnNamesCapitalization(SQLTemplate.UPPERCASE_COLUMN_NAMES);
+
+        EntityResult artistResult = new EntityResult(Artist.class);
+        artistResult.addDbField(Artist.ARTIST_ID_PK_COLUMN, "X");
+        artistResult.addObjectField(Artist.ARTIST_NAME_PROPERTY, "Y");
+        artistResult.addObjectField(Artist.DATE_OF_BIRTH_PROPERTY, "Z");
+
+        SQLResultSetMapping rsMap = new SQLResultSetMapping();
+        rsMap.addEntityResult(artistResult);
+        rsMap.addColumnResult("C");
+        query.setResultSetMapping(rsMap);
+
+        List objects = createDataContext().performQuery(query);
+        assertEquals(4, objects.size());
+
+        Object o1 = objects.get(0);
+        assertTrue("Expected Object[]: " + o1, o1 instanceof Object[]);
+        Object[] array1 = (Object[]) o1;
+        assertEquals(2, array1.length);
+        Object[] array2 = (Object[]) objects.get(1);
+        assertEquals(2, array2.length);
+        Object[] array3 = (Object[]) objects.get(2);
+        assertEquals(2, array3.length);
+        Object[] array4 = (Object[]) objects.get(3);
+        assertEquals(2, array3.length);
+        
+        assertEquals(new Integer(1), array1[1]);
+        assertEquals(new Integer(1), array2[1]);
+        assertEquals(new Integer(0), array3[1]);
+        assertEquals(new Integer(0), array4[1]);
+        assertTrue("Unexpected DataObject: " + array1[0], array1[0] instanceof Artist);
     }
 
     public void testSQLResultSetMappingScalar() throws Exception {



Mime
View raw message