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 <[email protected]>
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());