You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/02/22 11:06:10 UTC

cayenne git commit: CAY-2244 Using iterate() with ColumnSelect causes a ClassCastException

Repository: cayenne
Updated Branches:
  refs/heads/master cd83358e7 -> 7b90a4879


CAY-2244 Using iterate() with ColumnSelect causes a ClassCastException


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/7b90a487
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/7b90a487
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/7b90a487

Branch: refs/heads/master
Commit: 7b90a487997c05ade6db6ce199d1aafe66212122
Parents: cd83358
Author: Nikita Timofeev <st...@gmail.com>
Authored: Wed Feb 22 14:05:11 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Wed Feb 22 14:05:11 2017 +0300

----------------------------------------------------------------------
 .../org/apache/cayenne/access/DataContext.java  | 121 ++++++++++++-------
 .../cayenne/query/ObjectSelect_RunIT.java       |  57 +++++++++
 2 files changed, 131 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/7b90a487/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
index cc0a65e..264a7e8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -800,59 +800,35 @@ public class DataContext extends BaseContext {
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T> ResultIterator<T> iterator(Select<T> query) {
-        final ResultIterator<DataRow> rows = performIteratedQuery(query);
+    public <T> ResultIterator<T> iterator(final Select<T> query) {
+        final ResultIterator<?> rows = performIteratedQuery(query);
+        final QueryMetadata md = query.getMetaData(getEntityResolver());
 
-        QueryMetadata md = query.getMetaData(getEntityResolver());
-        if (md.isFetchingDataRows()) {
+        if (md.isFetchingDataRows() || isObjectArrayResult(md)) {
+            // no need to convert result
             return (ResultIterator<T>) rows;
         } else {
-
             // this is a bit optimized version of 'objectFromDataRow' with
             // resolver cached for reuse... still the rest is pretty suboptimal
-            ClassDescriptor descriptor = md.getClassDescriptor();
-            final ObjectResolver resolver = new ObjectResolver(this, descriptor, true);
-            return new ResultIterator<T>() {
-
-                @Override
-                public Iterator<T> iterator() {
-                    return new ResultIteratorIterator<T>(this);
-                }
-
-                @Override
-                public List<T> allRows() {
-                    List<T> list = new ArrayList<>();
-
-                    while (hasNextRow()) {
-                        list.add(nextRow());
-                    }
-
-                    return list;
-                }
-
-                @Override
-                public boolean hasNextRow() {
-                    return rows.hasNextRow();
-                }
-
-                @Override
-                public T nextRow() {
-                    DataRow row = rows.nextRow();
-                    List<T> objects = (List<T>) resolver
-                            .synchronizedObjectsFromDataRows(Collections.singletonList(row));
-                    return (T) objects.get(0);
-                }
+            final ObjectResolver resolver = new ObjectResolver(this, md.getClassDescriptor(), true);
+            return new DataRowResultIterator(rows, resolver);
+        }
+    }
 
-                @Override
-                public void skipRow() {
-                    rows.skipRow();
-                }
+    /**
+     * This method repeats logic of DataDomainQueryAction.interceptObjectConversion() method.
+     * The difference is that iterator(or batchIterator) doesn't support "mixed" results.
+     */
+    private boolean isObjectArrayResult(QueryMetadata md) {
+        List<Object> resultMapping = md.getResultSetMapping();
+        if(resultMapping == null) {
+            return false;
+        }
 
-                @Override
-                public void close() {
-                    rows.close();
-                }
-            };
+        if (md.isSingleResultSetMapping()) {
+            return !(resultMapping.get(0) instanceof EntityResultSegment);
+        } else {
+            return true;
         }
     }
 
@@ -1142,7 +1118,7 @@ public class DataContext extends BaseContext {
     }
 
     /**
-     * An internal version of {@link #localObject(Object)} that operates on
+     * An internal version of {@link #localObject(Persistent)} that operates on
      * ObjectId instead of Persistent, and wouldn't attempt to look up an object
      * in the parent channel.
      * 
@@ -1218,4 +1194,55 @@ public class DataContext extends BaseContext {
         this.transactionFactory = transactionFactory;
     }
 
+    /**
+     * ResultIterator that can convert DataRow to Persistent object on the fly.
+     */
+    static class DataRowResultIterator<T> implements ResultIterator<T> {
+
+        final ResultIterator<?> rows;
+        ObjectResolver resolver;
+
+        DataRowResultIterator(ResultIterator<?> rows, ObjectResolver resolver) {
+            this.rows = rows;
+            this.resolver = resolver;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return new ResultIteratorIterator<>(this);
+        }
+
+        @Override
+        public List<T> allRows() {
+            List<T> list = new ArrayList<>();
+            while (hasNextRow()) {
+                list.add(nextRow());
+            }
+            return list;
+        }
+
+        @Override
+        public boolean hasNextRow() {
+            return rows.hasNextRow();
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public T nextRow() {
+            DataRow row = (DataRow) rows.nextRow();
+            List<T> objects = (List<T>) resolver.synchronizedObjectsFromDataRows(Collections.singletonList(row));
+            return objects.get(0);
+        }
+
+        @Override
+        public void skipRow() {
+            rows.skipRow();
+        }
+
+        @Override
+        public void close() {
+            rows.close();
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/7b90a487/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
index eb4095d..32dd23e 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
@@ -290,4 +290,61 @@ public class ObjectSelect_RunIT extends ServerCase {
 		assertNotNull(a);
 		assertEquals("ng1 artist1", a);
 	}
+
+	@Test
+	public void test_ColumnSelect_IterationSingleColumn() throws Exception {
+		ColumnSelect<String> columnSelect = ObjectSelect.query(Artist.class).column(Artist.ARTIST_NAME);
+
+		final int[] count = new int[1];
+		columnSelect.iterate(context, new ResultIteratorCallback<String>() {
+			@Override
+			public void next(String object) {
+				count[0]++;
+				assertTrue(object.startsWith("artist"));
+			}
+		});
+
+		assertEquals(20, count[0]);
+	}
+
+	@Test
+	public void test_ColumnSelect_BatchIterationSingleColumn() throws Exception {
+		ColumnSelect<String> columnSelect = ObjectSelect.query(Artist.class).column(Artist.ARTIST_NAME);
+
+		try(ResultBatchIterator<String> it = columnSelect.batchIterator(context, 10)) {
+			List<String> next = it.next();
+			assertEquals(10, next.size());
+			assertTrue(next.get(0).startsWith("artist"));
+		}
+	}
+
+	@Test
+	public void test_ColumnSelect_IterationMultiColumns() throws Exception {
+		ColumnSelect<Object[]> columnSelect = ObjectSelect.query(Artist.class).columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
+
+		final int[] count = new int[1];
+		columnSelect.iterate(context, new ResultIteratorCallback<Object[]>() {
+			@Override
+			public void next(Object[] object) {
+				count[0]++;
+				assertTrue(object[0] instanceof String);
+				assertTrue(object[1] instanceof java.util.Date);
+			}
+		});
+
+		assertEquals(20, count[0]);
+	}
+
+	@Test
+	public void test_ColumnSelect_BatchIterationMultiColumns() throws Exception {
+		ColumnSelect<Object[]> columnSelect = ObjectSelect.query(Artist.class).columns(Artist.ARTIST_NAME, Artist.DATE_OF_BIRTH);
+
+		try(ResultBatchIterator<Object[]> it = columnSelect.batchIterator(context, 10)) {
+			List<Object[]> next = it.next();
+			assertEquals(10, next.size());
+			assertTrue(next.get(0)[0] instanceof String);
+			assertTrue(next.get(0)[1] instanceof java.util.Date);
+		}
+	}
+
 }