You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by sk...@apache.org on 2015/04/06 15:49:51 UTC

cayenne git commit: CAY-1995 | Add support for iterators to Select

Repository: cayenne
Updated Branches:
  refs/heads/master 44fdc8454 -> 39d58487c


CAY-1995 | Add support for iterators to Select


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

Branch: refs/heads/master
Commit: 39d58487cf16da839f609a05d83957ce94263be5
Parents: 44fdc84
Author: Savva Kolbachev <s....@gmail.com>
Authored: Mon Apr 6 16:47:56 2015 +0300
Committer: Savva Kolbachev <s....@gmail.com>
Committed: Mon Apr 6 16:47:56 2015 +0300

----------------------------------------------------------------------
 .../java/org/apache/cayenne/BaseContext.java    |  5 ++
 .../java/org/apache/cayenne/ObjectContext.java  | 11 +++
 .../org/apache/cayenne/ResultBatchIterator.java | 82 ++++++++++++++++++++
 .../java/org/apache/cayenne/ResultIterator.java |  4 +-
 .../org/apache/cayenne/query/ObjectSelect.java  |  5 ++
 .../org/apache/cayenne/query/SQLSelect.java     |  8 +-
 .../java/org/apache/cayenne/query/Select.java   | 12 +++
 .../org/apache/cayenne/query/SelectById.java    |  6 ++
 .../org/apache/cayenne/query/SelectQuery.java   |  6 ++
 .../cayenne/remote/IncrementalSelectQuery.java  |  6 ++
 .../org/apache/cayenne/MockBaseContext.java     | 11 ++-
 .../apache/cayenne/access/DataContextIT.java    | 21 +++++
 .../cayenne/query/ObjectSelect_RunIT.java       | 21 +++++
 .../org/apache/cayenne/query/SQLSelectIT.java   | 22 ++++++
 .../org/apache/cayenne/query/SelectQueryIT.java | 22 ++++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |  1 +
 16 files changed, 238 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java b/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
index 45881c9..174d266 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
@@ -352,6 +352,11 @@ public abstract class BaseContext implements ObjectContext {
     public abstract <T> ResultIterator<T> iterator(Select<T> query);
 
     @Override
+    public <T> ResultBatchIterator<T> batchIterator(Select<T> query, int size) {
+        return new ResultBatchIterator<T>(iterator(query), size);
+    }
+
+    @Override
     public void prepareForAccess(Persistent object, String property, boolean lazyFaulting) {
         if (object.getPersistenceState() == PersistenceState.HOLLOW) {
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/ObjectContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ObjectContext.java b/cayenne-server/src/main/java/org/apache/cayenne/ObjectContext.java
index 8f0c1dd..dc535f8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/ObjectContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ObjectContext.java
@@ -234,6 +234,17 @@ public interface ObjectContext extends DataChannel, Serializable {
     <T> ResultIterator<T> iterator(Select<T> query);
 
     /**
+     * Creates a ResultBatchIterator based on the provided query and batch size. It is usually
+     * backed by an open result set and is useful for processing of large data
+     * sets, preserving a constant memory footprint. The caller must wrap
+     * iteration in try/finally (or try-with-resources for Java 1.7 and higher) and
+     * close the ResultBatchIterator explicitly.
+     *
+     * @since 4.0
+     */
+    <T> ResultBatchIterator<T> batchIterator(Select<T> query, int size);
+
+    /**
      * Executes any kind of query providing the result in a form of
      * QueryResponse.
      */

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java b/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
new file mode 100644
index 0000000..5b130d8
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
@@ -0,0 +1,82 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Defines API of a batch iterator over the ResultIterator returned as a result of
+ * Select queries execution. Usually a ResultBatchIterator is supported by an open
+ * java.sql.ResultSet, therefore ResultBatchIterator must be explicitly closed when
+ * the user is done working with them.
+ *
+ * @since 4.0
+ */
+public class ResultBatchIterator<T> implements Iterable<T>, Closeable {
+
+    private ResultIterator delegate;
+    private int size;
+
+    public ResultBatchIterator(ResultIterator delegate, int size) {
+        this.delegate = delegate;
+        this.size = size;
+    }
+
+    /**
+     * Returns the next batch of result rows, depending on the query and batch size, may be a
+     * List of scalar values, DataRows, or Object[] arrays containing a mix of scalars and DataRows.
+     *
+     * @since 4.0
+     */
+    public List<T> nextBatch() {
+        List<T> objects = new ArrayList<T>(size);
+        int i = 0;
+
+        while (i < size) {
+            if (delegate.hasNextRow()) {
+                objects.add((T) delegate.nextRow());
+                i++;
+            } else {
+                break;
+            }
+        }
+
+        return objects;
+    };
+
+    @Override
+    public Iterator<T> iterator() {
+        return delegate.iterator();
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    public int getBatchSize() {
+        return size;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java b/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
index 060004f..3de04f0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
@@ -24,11 +24,13 @@ import java.util.List;
 
 /**
  * Defines API of an iterator over the records returned as a result of
- * SelectQuery execution. Usually a ResultIterator is supported by an open
+ * Select queries execution. Usually a ResultIterator is supported by an open
  * java.sql.ResultSet, therefore ResultIterators must be explicitly closed when
  * the user is done working with them. An alternative to that is
  * {@link ObjectContext#iterate(org.apache.cayenne.query.Select, ResultIteratorCallback)}
  * method that handles resource management.
+ *
+ * @since 3.0
  */
 public interface ResultIterator<T> extends Iterable<T>, Closeable {
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
index 5d7172d..515a4c5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/ObjectSelect.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.query;
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.exp.Expression;
@@ -679,4 +680,8 @@ public class ObjectSelect<T> extends IndirectQuery implements Select<T> {
         return context.iterator(this);
     }
 
+    @Override
+    public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+        return context.batchIterator(this, size);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
index e93fc50..4f54648 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SQLSelect.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.query;
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.map.DataMap;
@@ -139,7 +140,12 @@ public class SQLSelect<T> extends IndirectQuery implements Select<T> {
         return context.iterator(this);
     }
 
-	public boolean isFetchingDataRows() {
+    @Override
+    public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+        return context.batchIterator(this, size);
+    }
+
+    public boolean isFetchingDataRows() {
 		return persistentType == null;
 	}
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/query/Select.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/Select.java b/cayenne-server/src/main/java/org/apache/cayenne/query/Select.java
index f744d5c..b4380ba 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/Select.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/Select.java
@@ -20,6 +20,7 @@
 package org.apache.cayenne.query;
 
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 
@@ -95,4 +96,15 @@ public interface Select<T> extends Query {
      * @since 4.0
      */
     <T> ResultIterator<T> iterator(ObjectContext context);
+
+    /**
+     * Creates a ResultBatchIterator based on the provided context and batch size. It is usually
+     * backed by an open result set and is useful for processing of large data
+     * sets, preserving a constant memory footprint. The caller must wrap
+     * iteration in try/finally (or try-with-resources for Java 1.7 and higher) and
+     * close the ResultBatchIterator explicitly.
+     *
+     * @since 4.0
+     */
+    <T> ResultBatchIterator<T> batchIterator(ObjectContext context, int size);
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java
index 50c0750..78e0146 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java
@@ -22,6 +22,7 @@ import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.exp.Expression;
@@ -162,6 +163,11 @@ public class SelectById<T> extends IndirectQuery implements Select<T> {
         return context.iterator(this);
     }
 
+    @Override
+    public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+        return context.batchIterator(this, size);
+    }
+
     /**
 	 * Instructs Cayenne to look for query results in the "local" cache when
 	 * running the query. This is a short-hand notation for:

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
index f116af7..db012b3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQuery.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.query;
 
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.exp.Expression;
@@ -333,6 +334,11 @@ public class SelectQuery<T> extends AbstractQuery implements ParameterizedQuery,
         return context.iterator(this);
     }
 
+    @Override
+    public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+        return context.batchIterator(this, size);
+    }
+
     /**
 	 * @since 1.2
 	 */

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
index a1cd059..7da3793 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/remote/IncrementalSelectQuery.java
@@ -19,6 +19,7 @@
 package org.apache.cayenne.remote;
 
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.access.IncrementalFaultList;
@@ -368,4 +369,9 @@ class IncrementalSelectQuery<T> extends SelectQuery<T> {
     public ResultIterator<T> iterator(ObjectContext context) {
         return query.iterator(context);
     }
+
+    @Override
+    public ResultBatchIterator<T> batchIterator(ObjectContext context, int size) {
+        return query.batchIterator(context, size);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/test/java/org/apache/cayenne/MockBaseContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/MockBaseContext.java b/cayenne-server/src/test/java/org/apache/cayenne/MockBaseContext.java
index 3e0617c..ad59c99 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/MockBaseContext.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/MockBaseContext.java
@@ -18,15 +18,15 @@
  ****************************************************************/
 package org.apache.cayenne;
 
-import java.util.Collection;
-import java.util.List;
-
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.graph.GraphManager;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.query.Select;
 
+import java.util.Collection;
+import java.util.List;
+
 public class MockBaseContext extends BaseContext {
 
     @Override
@@ -116,4 +116,9 @@ public class MockBaseContext extends BaseContext {
         return null;
     }
 
+    @Override
+    public <T> ResultBatchIterator<T> batchIterator(Select<T> query, int size) {
+        return null;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextIT.java
index 4c0b8db..a1c0caf 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextIT.java
@@ -25,6 +25,7 @@ import org.apache.cayenne.DataRow;
 import org.apache.cayenne.Fault;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.conn.PoolManager;
@@ -675,6 +676,26 @@ public class DataContextIT extends ServerCase {
     }
 
     @Test
+    public void testBatchIterator() throws Exception {
+        createArtistsDataSet();
+
+        SelectQuery<Artist> q1 = new SelectQuery<Artist>(Artist.class);
+
+        ResultBatchIterator<Artist> it = context.batchIterator(q1, 4);
+        try {
+
+            List<Artist> firstBatch = it.nextBatch();
+            assertEquals(4, firstBatch.size());
+
+            List<Artist> secondBatch = it.nextBatch();
+            assertEquals(3, secondBatch.size());
+
+        } finally {
+            it.close();
+        }
+    }
+
+    @Test
     public void testPerformIteratedQuery1() throws Exception {
 
         createArtistsDataSet();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/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 8cd551d..a856dca 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
@@ -20,6 +20,7 @@ package org.apache.cayenne.query;
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.access.DataContext;
@@ -110,6 +111,26 @@ public class ObjectSelect_RunIT extends ServerCase {
         }
     }
 
+    @Test
+    public void test_BatchIterator() throws Exception {
+        createArtistsDataSet();
+
+        ResultBatchIterator<Artist> it = ObjectSelect.query(Artist.class).batchIterator(context, 7);
+
+        try {
+            List<Artist> firstBatch = it.nextBatch();
+            assertEquals(7, firstBatch.size());
+
+            List<Artist> secondBatch = it.nextBatch();
+            assertEquals(7, secondBatch.size());
+
+            List<Artist> thirdBatch = it.nextBatch();
+            assertEquals(6, thirdBatch.size());
+        } finally {
+            it.close();
+        }
+    }
+
 	@Test
 	public void test_SelectDataRows() throws Exception {
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java
index 0061a84..55d8d94 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SQLSelectIT.java
@@ -19,6 +19,7 @@
 package org.apache.cayenne.query;
 
 import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.access.DataContext;
@@ -259,6 +260,27 @@ public class SQLSelectIT extends ServerCase {
         }
     }
 
+    @Test
+    public void test_BatchIterator() throws Exception {
+        createPaintingsDataSet();
+
+        ResultBatchIterator<Painting> it = SQLSelect.query(Painting.class, "SELECT * FROM PAINTING")
+                .columnNameCaps(CapsStrategy.UPPER).batchIterator(context, 7);
+
+        try {
+            List<Painting> firstBatch = it.nextBatch();
+            assertEquals(7, firstBatch.size());
+
+            List<Painting> secondBatch = it.nextBatch();
+            assertEquals(7, secondBatch.size());
+
+            List<Painting> thirdBatch = it.nextBatch();
+            assertEquals(6, thirdBatch.size());
+        } finally {
+            it.close();
+        }
+    }
+
 	@Test
 	public void test_SelectLong() throws Exception {
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
index 785e1a7..32a5280 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.query;
 import org.apache.cayenne.Cayenne;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.ResultBatchIterator;
 import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.ResultIteratorCallback;
 import org.apache.cayenne.di.Inject;
@@ -573,6 +574,27 @@ public class SelectQueryIT extends ServerCase {
         }
     }
 
+    @Test
+    public void testBatchIterator() throws Exception {
+        createArtistsDataSet();
+
+        SelectQuery<Artist> q1 = new SelectQuery<Artist>(Artist.class);
+        ResultBatchIterator<Artist> it = q1.batchIterator(context, 7);
+
+        try {
+            List<Artist> firstBatch = it.nextBatch();
+            assertEquals(7, firstBatch.size());
+
+            List<Artist> secondBatch = it.nextBatch();
+            assertEquals(7, secondBatch.size());
+
+            List<Artist> thirdBatch = it.nextBatch();
+            assertEquals(6, thirdBatch.size());
+        } finally {
+            it.close();
+        }
+    }
+
 	/**
 	 * Tests that all queries specified in prefetch are executed in a more
 	 * complex prefetch scenario.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/39d58487/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 2746800..66d3f09 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -16,6 +16,7 @@ Changes/New Features:
 
 CAY-1991 More control over generated String property names
 CAY-1992 Allow to exclude DataMap java class from Modeler class generation
+CAY-1995 Add support for iterators to Select
 
 Bug Fixes: