This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new ab136bb43 CAY-2814 Select query iterator() and batchIterator() methods 
return incorrect results  - major code cleanup and refactoring
ab136bb43 is described below

commit ab136bb436c222ac1f8408ba596abbac0c148d46
Author: stariy95 <stari...@gmail.com>
AuthorDate: Wed Nov 8 16:55:35 2023 +0400

    CAY-2814 Select query iterator() and batchIterator() methods return 
incorrect results
     - major code cleanup and refactoring
---
 .../org/apache/cayenne/access/DataContext.java     |   3 +-
 .../cayenne/access/DataDomainQueryAction.java      | 234 +++++++++------------
 .../access/ResultIteratorConverterDecorator.java   |  25 ++-
 .../apache/cayenne/util/IteratedQueryResponse.java |  65 ------
 .../apache/cayenne/util/GenericResponseTest.java   |  14 +-
 .../org/apache/cayenne/util/ListResponseTest.java  |  15 +-
 6 files changed, 137 insertions(+), 219 deletions(-)

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 347eaf7bf..ec930c81a 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
@@ -798,10 +798,9 @@ public class DataContext extends BaseContext {
         IteratedQueryDecorator queryDecorator = new 
IteratedQueryDecorator(query);
         Query queryToRun = nonNullDelegate().willPerformQuery(this, 
queryDecorator);
         QueryResponse queryResponse = onQuery(this, queryToRun);
-        return (ResultIterator<T>) queryResponse.currentIterator();
+        return (ResultIterator<T>) queryResponse.firstIterator();
     }
 
-
     /**
      * Performs a single database select query returning result as a
      * ResultIterator. It is caller's responsibility to explicitly close the
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
index d13f06138..f1b01a4ba 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
@@ -54,7 +54,6 @@ import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
 import org.apache.cayenne.tx.BaseTransaction;
 import org.apache.cayenne.tx.Transaction;
 import org.apache.cayenne.util.GenericResponse;
-import org.apache.cayenne.util.IteratedQueryResponse;
 import org.apache.cayenne.util.ListResponse;
 import org.apache.cayenne.util.Util;
 import org.slf4j.Logger;
@@ -97,7 +96,6 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
     private Map<QueryEngine, Collection<Query>> queriesByNode;
     private Map<Query, Query> queriesByExecutedQueries;
     private boolean noObjectConversion;
-    private IteratedQueryResponse iteratedQueryResponse;
 
     /*
      * A constructor for the "new" way of performing a query via 'execute' with
@@ -159,7 +157,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
     }
 
     private void validateIteratedQuery() {
-        if (metadata.getPageSize() > 0){
+        if (metadata.getPageSize() > 0) {
             throw new CayenneRuntimeException("Pagination is not supported 
with iterator");
         }
         if (metadata.getPrefetchTree() != null) {
@@ -167,7 +165,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                 if (prefetchTreeNode.isDisjointPrefetch()) {
                     throw new CayenneRuntimeException("\"Disjoint\" semantic 
doesn't work with iterator. Use \"Joint\" instead");
                 }
-                if (prefetchTreeNode.isDisjointByIdPrefetch()){
+                if (prefetchTreeNode.isDisjointByIdPrefetch()) {
                     LOGGER.warn("A separate select query will be created for 
each iterated item");
                 }
             }
@@ -224,15 +222,13 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
             DataRow row = null;
 
-                       if (cache != null && !oidQuery.isFetchMandatory()) {
-                               row = polymorphicRowFromCache(oid);
-                       }
+            if (cache != null && !oidQuery.isFetchMandatory()) {
+                row = polymorphicRowFromCache(oid);
+            }
 
             // refresh is forced or not found in cache
             if (row == null) {
-
                 if (oidQuery.isFetchAllowed()) {
-
                     runQueryInTransaction();
                 } else {
                     response = new ListResponse();
@@ -246,38 +242,38 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
         return !DONE;
     }
-    
-       private DataRow polymorphicRowFromCache(ObjectId superOid) {
-               DataRow row = cache.getCachedSnapshot(superOid);
-               if (row != null) {
-                       return row;
-               }
-
-               EntityInheritanceTree inheritanceTree = 
domain.getEntityResolver().getInheritanceTree(superOid.getEntityName());
-               if (!inheritanceTree.getChildren().isEmpty()) {
-                       row = polymorphicRowFromCache(inheritanceTree, 
superOid);
-               }
-
-               return row;
-       }
-    
-       private DataRow polymorphicRowFromCache(EntityInheritanceTree 
superNode, ObjectId superOid) {
-
-               for (EntityInheritanceTree child : superNode.getChildren()) {
-                       ObjectId id = ObjectId.of(child.getEntity().getName(), 
superOid);
-                       DataRow row = cache.getCachedSnapshot(id);
-                       if (row != null) {
-                               return row;
-                       }
-                       
-                       row = polymorphicRowFromCache(child, superOid);
-                       if (row != null) {
-                               return row;
-                       }
-               }
-
-               return null;
-       }
+
+    private DataRow polymorphicRowFromCache(ObjectId superOid) {
+        DataRow row = cache.getCachedSnapshot(superOid);
+        if (row != null) {
+            return row;
+        }
+
+        EntityInheritanceTree inheritanceTree = 
domain.getEntityResolver().getInheritanceTree(superOid.getEntityName());
+        if (!inheritanceTree.getChildren().isEmpty()) {
+            row = polymorphicRowFromCache(inheritanceTree, superOid);
+        }
+
+        return row;
+    }
+
+    private DataRow polymorphicRowFromCache(EntityInheritanceTree superNode, 
ObjectId superOid) {
+
+        for (EntityInheritanceTree child : superNode.getChildren()) {
+            ObjectId id = ObjectId.of(child.getEntity().getName(), superOid);
+            DataRow row = cache.getCachedSnapshot(id);
+            if (row != null) {
+                return row;
+            }
+
+            row = polymorphicRowFromCache(child, superOid);
+            if (row != null) {
+                return row;
+            }
+        }
+
+        return null;
+    }
 
     private boolean interceptRelationshipQuery() {
 
@@ -319,7 +315,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
             // null id means that FK is null...
             if (targetId == null) {
-                this.response = new GenericResponse(Collections.EMPTY_LIST);
+                this.response = new GenericResponse(Collections.emptyList());
                 return DONE;
             }
 
@@ -331,15 +327,11 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                 return DONE;
             }
 
-            // check whether a non-null FK is enough to assume non-null target,
-            // and if so,
-            // create a fault
+            // check whether a non-null FK is enough to assume non-null 
target, and if so, create a fault
             if (context != null && 
relationship.isSourceDefiningTargetPrecenseAndType(domain.getEntityResolver())) 
{
 
-                // prevent passing partial snapshots to ObjectResolver per
-                // CAY-724.
-                // Create a hollow object right here and skip object conversion
-                // downstream
+                // prevent passing partial snapshots to ObjectResolver per 
CAY-724.
+                // Create a hollow object right here and skip object 
conversion downstream
                 this.noObjectConversion = true;
                 Object object = context.findOrCreateObject(targetId);
 
@@ -354,6 +346,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
     /**
      * @since 3.0
      */
+    @SuppressWarnings("deprecation")
     private boolean interceptRefreshQuery() {
 
         if (query instanceof RefreshQuery) {
@@ -403,8 +396,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                 return DONE;
             }
 
-            // 3. refresh query - this shouldn't normally happen as child
-            // datacontext
+            // 3. refresh query - this shouldn't normally happen as child 
datacontext
             // usually does a cascading refresh
             if (refreshQuery.getQuery() != null) {
                 Query cachedQuery = refreshQuery.getQuery();
@@ -455,11 +447,10 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
         if (cache) {
             boolean wasResponseNull = (response == null);
-            List cachedResults = queryCache.get(metadata, factory);
+            List<?> cachedResults = queryCache.get(metadata, factory);
 
-            // response may already be initialized by the factory above ... it
-            // is null if
-            // there was a preexisting cache entry
+            // response may already be initialized by the factory above ...
+            // it is null if there was a preexisting cache entry
             if (response == null || wasResponseNull) {
                 response = new ListResponse(cachedResults);
             }
@@ -468,8 +459,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                 this.prefetchResultsByPath = ((ListWithPrefetches) 
cachedResults).getPrefetchResultsByPath();
             }
         } else {
-            // on cache-refresh request, fetch without blocking and fill the
-            // cache
+            // on cache-refresh request, fetch without blocking and fill the 
cache
             queryCache.put(metadata, factory.createObject());
         }
 
@@ -530,72 +520,44 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
         }
     }
 
-    private void runIteratedQuery(Transaction tx){
-        // reset
-        this.fullResponse = null;
-        this.response = null;
-        this.queriesByNode = null;
-        this.queriesByExecutedQueries = null;
-
-        // whether this is null or not will driver further decisions on how to 
process prefetched rows
-        this.prefetchResultsByPath = metadata.getPrefetchTree() != null && 
!metadata.isFetchingDataRows()
-                ? new HashMap<>() : null;
-
-        // categorize queries by node and by "executable" query...
-        query.route(this, domain.getEntityResolver(), null);
-
-        // run categorized queries
-        if (queriesByNode != null) {
-            for (Map.Entry<QueryEngine, Collection<Query>> entry : 
queriesByNode.entrySet()) {
-                QueryEngine nextNode = entry.getKey();
-                Collection<Query> nodeQueries = entry.getValue();
-                nextNode.performQueries(nodeQueries, this);
-            }
+    private void runIteratedQuery(Transaction tx) {
+        runQuery();
+        ResultIterator<?> iterator = fullResponse.firstIterator();
+        if (iterator == null) {
+            throw new IllegalStateException("Iterator response expected");
         }
-
-        wrapResponseIteratorWithTransactionDecorator(tx);
-        this.fullResponse = iteratedQueryResponse;
-        this.response = iteratedQueryResponse;
-    }
-
-    private void wrapResponseIteratorWithTransactionDecorator(Transaction tx){
-        ResultIterator<?> iterator = iteratedQueryResponse.currentIterator();
-        TransactionResultIteratorDecorator<?> decorator = new 
TransactionResultIteratorDecorator<>(iterator, tx);
-        iteratedQueryResponse.setIterator(decorator);
-    }
-
-    private void 
wrapResponseIteratorWithConverterDecorator(ObjectConversionStrategy<?, ?> 
converter) {
-        ResultIterator<?> iterator = response.currentIterator();
-        ResultIteratorConverterDecorator decorator = new 
ResultIteratorConverterDecorator(iterator, converter);
-        iteratedQueryResponse.setIterator(decorator);
+        fullResponse.replaceResult(iterator, new 
TransactionResultIteratorDecorator<>(iterator, tx));
+        fullResponse.reset();
     }
 
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked", "rawtypes"})
     private void interceptObjectConversion() {
-        if (context != null) {
-            ObjectConversionStrategy<?,?> converter = getConverter();
-            ResultIterator<?> iterator = response.firstIterator();
-            if (iterator != null) {
-                wrapResponseIteratorWithConverterDecorator(converter);
-            } else {
-                List mainRows = response.firstList(); // List<DataRow> or 
List<Object[]>
+        if (context == null) {
+            return;
+        }
+
+        ObjectConversionStrategy<?, ?> converter = getConverter();
+        // local copy because it can change while iterating
+        QueryResponse response = this.response;
+        for (response.reset(); response.next();) {
+            if (response.isList()) {
+                List<?> mainRows = response.currentList(); // List<DataRow> or 
List<Object[]>
                 if (mainRows != null && !mainRows.isEmpty()) {
-                    converter.convert(mainRows);
-                    rewindResponseAfterFirstListCall();
+                    converter.convert((List) mainRows);
                 }
+            } else if (response.isIterator()) {
+                // iterator should be a part of full response
+                ResultIterator<?> iterator = fullResponse.currentIterator();
+                fullResponse.replaceResult(iterator, new 
ResultIteratorConverterDecorator(iterator, converter));
             }
         }
-    }
-
-
-    private void rewindResponseAfterFirstListCall() {
         response.reset();
     }
 
-    private ObjectConversionStrategy<?,?> getConverter() {
-        ObjectConversionStrategy<?,?> converter;
+    private ObjectConversionStrategy<?, ?> getConverter() {
+        ObjectConversionStrategy<?, ?> converter;
 
-        if(metadata.isFetchingDataRows()) {
+        if (metadata.isFetchingDataRows()) {
             converter = new IdentityConversionStrategy();
         } else {
             List<Object> rsMapping = metadata.getResultSetMapping();
@@ -605,8 +567,8 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                 if (metadata.isSingleResultSetMapping()) {
                     if (rsMapping.get(0) instanceof EntityResultSegment) {
                         converter = new SingleObjectConversionStrategy();
-                } else if(rsMapping.get(0) instanceof EmbeddableResultSegment) 
{
-                    converter = new SingleEmbeddableConversionStrategy();
+                    } else if (rsMapping.get(0) instanceof 
EmbeddableResultSegment) {
+                        converter = new SingleEmbeddableConversionStrategy();
                     } else {
                         converter = new SingleScalarConversionStrategy();
                     }
@@ -616,7 +578,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
             }
         }
 
-        if(metadata.getResultMapper() != null) {
+        if (metadata.getResultMapper() != null) {
             converter = new MapperConversionStrategy(converter);
         }
         return converter;
@@ -639,9 +601,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
         queries.add(query);
 
-        // handle case when routing resulted in an "executable" query different
-        // from the
-        // original query.
+        // handle case when routing resulted in an "executable" query 
different from the original query.
         if (substitutedQuery != null && substitutedQuery != query) {
 
             if (queriesByExecutedQueries == null) {
@@ -702,10 +662,9 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
     @Override
     public void nextRows(Query query, List<?> dataRows) {
-
         // exclude prefetched rows in the main result
         if (prefetchResultsByPath != null && query instanceof 
PrefetchSelectQuery) {
-            PrefetchSelectQuery prefetchQuery = (PrefetchSelectQuery) query;
+            PrefetchSelectQuery<?> prefetchQuery = (PrefetchSelectQuery<?>) 
query;
             prefetchResultsByPath.put(prefetchQuery.getPrefetchPath(), 
dataRows);
         } else {
             fullResponse.addResultList(dataRows);
@@ -714,13 +673,12 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
     @Override
     public void nextRows(Query q, ResultIterator<?> it) {
-        iteratedQueryResponse = new IteratedQueryResponse(it);
         // exclude prefetched rows in the main result
         if (prefetchResultsByPath != null && query instanceof 
PrefetchSelectQuery) {
-            PrefetchSelectQuery prefetchQuery = (PrefetchSelectQuery) query;
+            PrefetchSelectQuery<?> prefetchQuery = (PrefetchSelectQuery<?>) 
query;
             prefetchResultsByPath.put(prefetchQuery.getPrefetchPath(), 
(List<?>) it);
         } else {
-            this.fullResponse = iteratedQueryResponse;
+            this.fullResponse.addResultIterator(it);
         }
     }
 
@@ -748,13 +706,13 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
         return (query instanceof IteratedQueryDecorator);
     }
 
-     abstract class ObjectConversionStrategy<T, R> {
+    abstract class ObjectConversionStrategy<T, R> {
         abstract void convert(List<T> mainRows);
 
         abstract R convert(T t);
 
         protected PrefetchProcessorNode toResultsTree(ClassDescriptor 
descriptor, PrefetchTreeNode prefetchTree,
-                List<DataRow> normalizedRows) {
+                                                      List<DataRow> 
normalizedRows) {
 
             // take a shortcut when no prefetches exist...
             if (prefetchTree == null) {
@@ -767,7 +725,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
             }
         }
 
-        protected void updateResponse(List sourceObjects, List targetObjects) {
+        protected void updateResponse(List<T> sourceObjects, List<? extends R> 
targetObjects) {
             if (response instanceof GenericResponse) {
                 ((GenericResponse) response).replaceResult(sourceObjects, 
targetObjects);
             } else if (response instanceof ListResponse) {
@@ -810,7 +768,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
         }
 
         @Override
-         Object convert(DataRow dataRow) {
+        Object convert(DataRow dataRow) {
             PrefetchProcessorNode node = 
getPrefetchProcessorNode(Collections.singletonList(dataRow));
             return node.getObjects().get(0);
         }
@@ -820,8 +778,8 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
             List<Object> rsMapping = metadata.getResultSetMapping();
             EntityResultSegment resultSegment = null;
-            if(rsMapping != null && !rsMapping.isEmpty()) {
-                resultSegment = (EntityResultSegment)rsMapping.get(0);
+            if (rsMapping != null && !rsMapping.isEmpty()) {
+                resultSegment = (EntityResultSegment) rsMapping.get(0);
             }
 
             ClassDescriptor descriptor = resultSegment == null
@@ -850,14 +808,14 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
         @Override
         void convert(List<DataRow> mainRows) {
-            EmbeddableResultSegment resultSegment = 
(EmbeddableResultSegment)metadata.getResultSetMapping().get(0);
+            EmbeddableResultSegment resultSegment = (EmbeddableResultSegment) 
metadata.getResultSetMapping().get(0);
             Embeddable embeddable = resultSegment.getEmbeddable();
             Class<?> embeddableClass = 
objectFactory.getJavaClass(embeddable.getClassName());
             List<EmbeddableObject> result = new ArrayList<>(mainRows.size());
             mainRows.forEach(dataRow -> {
                 EmbeddableObject eo;
                 try {
-                    eo = 
(EmbeddableObject)embeddableClass.getDeclaredConstructor().newInstance();
+                    eo = (EmbeddableObject) 
embeddableClass.getDeclaredConstructor().newInstance();
                 } catch (Exception e) {
                     throw new CayenneRuntimeException("Unable to materialize 
embeddable '%s'", e, embeddable.getClassName());
                 }
@@ -877,7 +835,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
     class MixedConversionStrategy extends ObjectConversionStrategy<Object[], 
Object[]> {
 
         protected PrefetchProcessorNode toResultsTree(ClassDescriptor 
descriptor, PrefetchTreeNode prefetchTree,
-                List<Object[]> rows, int position) {
+                                                      List<Object[]> rows, int 
position) {
 
             List<DataRow> rowsColumn = new ArrayList<>(rows.size());
             for (Object[] row : rows) {
@@ -936,13 +894,13 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                         row[i] = objects.get(j);
                     }
                 } else if (mapping instanceof EmbeddableResultSegment) {
-                    EmbeddableResultSegment resultSegment = 
(EmbeddableResultSegment)mapping;
+                    EmbeddableResultSegment resultSegment = 
(EmbeddableResultSegment) mapping;
                     Embeddable embeddable = resultSegment.getEmbeddable();
                     Class<?> embeddableClass = 
objectFactory.getJavaClass(embeddable.getClassName());
                     try {
-                        for(Object[] row : mainRows) {
-                            DataRow dataRow = (DataRow)row[i];
-                            EmbeddableObject eo = 
(EmbeddableObject)embeddableClass.getDeclaredConstructor().newInstance();
+                        for (Object[] row : mainRows) {
+                            DataRow dataRow = (DataRow) row[i];
+                            EmbeddableObject eo = (EmbeddableObject) 
embeddableClass.getDeclaredConstructor().newInstance();
                             dataRow.forEach(eo::writePropertyDirectly);
                             row[i] = eo;
                         }
@@ -952,7 +910,7 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
                 }
             }
 
-            if(!metadata.isSuppressingDistinct()) {
+            if (!metadata.isSuppressingDistinct()) {
                 Set<List<?>> seen = new HashSet<>(mainRows.size());
                 mainRows.removeIf(objects -> 
!seen.add(Arrays.asList(objects)));
             }
@@ -998,8 +956,8 @@ class DataDomainQueryAction implements QueryRouter, 
OperationObserver {
 
         @SuppressWarnings({"unchecked", "rawtypes"})
         MapperConversionStrategy(ObjectConversionStrategy<?, ?> 
parentStrategy) {
-            this.mapper = (Function)metadata.getResultMapper();
-            this.parentStrategy = (ObjectConversionStrategy)parentStrategy;
+            this.mapper = (Function) metadata.getResultMapper();
+            this.parentStrategy = (ObjectConversionStrategy) parentStrategy;
         }
 
         @Override
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/access/ResultIteratorConverterDecorator.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/access/ResultIteratorConverterDecorator.java
index 9c26e62ee..08fb54380 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/ResultIteratorConverterDecorator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/ResultIteratorConverterDecorator.java
@@ -25,18 +25,21 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 
- class ResultIteratorConverterDecorator implements ResultIterator {
-    private final ResultIterator iterator;
-    private final DataDomainQueryAction.ObjectConversionStrategy converter;
+class ResultIteratorConverterDecorator<T, R> implements ResultIterator<R> {
+    private final ResultIterator<T> iterator;
+    private final DataDomainQueryAction.ObjectConversionStrategy<T, R> 
converter;
 
-     ResultIteratorConverterDecorator(ResultIterator iterator, 
DataDomainQueryAction.ObjectConversionStrategy converter) {
+    ResultIteratorConverterDecorator(ResultIterator<T> iterator, 
DataDomainQueryAction.ObjectConversionStrategy<T, R> converter) {
         this.iterator = Objects.requireNonNull(iterator);
         this.converter = Objects.requireNonNull(converter);
     }
 
+    @SuppressWarnings("unchecked")
     @Override
-    public List allRows() {
-        return iterator.allRows();
+    public List<R> allRows() {
+        List<T> mainRows = iterator.allRows();
+        converter.convert(mainRows);
+        return (List<R>) mainRows;
     }
 
     @Override
@@ -45,7 +48,7 @@ import java.util.Objects;
     }
 
     @Override
-    public Object nextRow() {
+    public R nextRow() {
         return converter.convert(iterator.nextRow());
     }
 
@@ -60,17 +63,17 @@ import java.util.Objects;
     }
 
     @Override
-    public Iterator iterator() {
-        return new Iterator() {
+    public Iterator<R> iterator() {
+        return new Iterator<>() {
             @Override
             public boolean hasNext() {
                 return iterator.hasNextRow();
             }
 
             @Override
-            public Object next() {
+            public R next() {
                 return converter.convert(iterator.nextRow());
             }
         };
     }
- }
+}
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/util/IteratedQueryResponse.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/util/IteratedQueryResponse.java
deleted file mode 100644
index 72e9913b2..000000000
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/util/IteratedQueryResponse.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************
- *   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
- *
- *    https://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.util;
-
-import org.apache.cayenne.ResultIterator;
-
-/**
- * Implementation of QueryResponse for iterated query.
- *
- * @since 5.0
- */
-public class IteratedQueryResponse extends GenericResponse {
-    private ResultIterator<?> iterator;
-
-    public IteratedQueryResponse(ResultIterator<?> iterator) {
-        this.iterator = iterator;
-    }
-
-    public void setIterator(ResultIterator<?> iterator) {
-        this.iterator = iterator;
-    }
-
-    @Override
-    public int size() {
-        return -1;
-    }
-
-    @Override
-    public boolean isIterator() {
-        return true;
-    }
-
-    @Override
-    public ResultIterator<?> currentIterator() {
-        return iterator;
-    }
-
-    @Override
-    public boolean next() {
-        return false;
-    }
-
-    @Override
-    public ResultIterator<?> firstIterator() {
-        return iterator;
-    }
-
-}
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/util/GenericResponseTest.java 
b/cayenne-server/src/test/java/org/apache/cayenne/util/GenericResponseTest.java
index 6184d4b14..f55d0b846 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/util/GenericResponseTest.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/util/GenericResponseTest.java
@@ -33,7 +33,7 @@ import static org.junit.Assert.assertTrue;
 public class GenericResponseTest {
 
     @Test
-    public void testCreation() throws Exception {
+    public void testCreation() {
         List list = new ArrayList();
         list.add(new HashMap());
 
@@ -58,6 +58,18 @@ public class GenericResponseTest {
         assertFalse(r.next());
     }
 
+    @Test
+    public void testNext() {
+        List<Integer> result = List.of(1, 2, 3);
+        GenericResponse r = new GenericResponse();
+        r.addResultList(result);
+
+        assertTrue(r.next());
+        assertTrue(r.isList());
+        assertEquals(result, r.currentList());
+        assertFalse(r.next());
+    }
+
     @Test
     public void testSerialization() throws Exception {
         List<Object> list = new ArrayList<>();
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/util/ListResponseTest.java 
b/cayenne-server/src/test/java/org/apache/cayenne/util/ListResponseTest.java
index 60cf91a57..3fe1901a2 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/util/ListResponseTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/util/ListResponseTest.java
@@ -32,7 +32,7 @@ import static org.junit.Assert.assertTrue;
 public class ListResponseTest {
 
     @Test
-    public void testCreation() throws Exception {
+    public void testCreation() {
 
         Object object = new Object();
         ListResponse r = new ListResponse(object);
@@ -54,12 +54,23 @@ public class ListResponseTest {
         assertSame(currentList, r.firstList());
     }
 
+    @Test
+    public void testNext() {
+        List<Integer> result = List.of(1, 2, 3);
+        ListResponse r = new ListResponse(result);
+
+        assertTrue(r.next());
+        assertTrue(r.isList());
+        assertEquals(result, r.currentList());
+        assertFalse(r.next());
+    }
+
     @Test
     public void testSerialization() throws Exception {
 
         ListResponse r = new ListResponse(67);
 
-        ListResponse sr = (ListResponse) Util.cloneViaSerialization(r);
+        ListResponse sr = Util.cloneViaSerialization(r);
         assertNotNull(sr);
         assertEquals(1, sr.size());
 

Reply via email to