You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2026/09/12 18:30:42 UTC
(cayenne) 02/02: CAY-3024 Replace QueryResponse with List
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 4b3133f90eb5ab7146bfa7f196bc96a0f4ff6207
Author: Andrus Adamchik <an...@objectstyle.com>
AuthorDate: Sat Sep 12 14:11:52 2026 -0400
CAY-3024 Replace QueryResponse with List<QueryResultItem>
---
RELEASE-NOTES.txt | 1 +
UPGRADE.md | 14 +-
.../apache/cayenne/lifecycle/id/StringIdQuery.java | 4 +-
.../relationship/ObjectIdRelationshipFilter.java | 8 +-
.../cayenne/lifecycle/id/StringIdQueryTest.java | 17 +-
.../main/java/org/apache/cayenne/DataChannel.java | 11 +-
.../org/apache/cayenne/DataChannelQueryFilter.java | 14 +-
.../cayenne/DataChannelQueryFilterChain.java | 4 +-
.../java/org/apache/cayenne/ObjectContext.java | 6 +-
.../java/org/apache/cayenne/QueryResponse.java | 165 -----------------
.../org/apache/cayenne/access/DataContext.java | 21 +--
.../apache/cayenne/access/DataContextChannel.java | 25 ++-
.../cayenne/access/DataContextQueryAction.java | 18 +-
.../java/org/apache/cayenne/access/DataDomain.java | 10 +-
.../cayenne/access/DataDomainIdQueryAction.java | 2 +-
.../cayenne/access/DataDomainQueryAction.java | 88 ++++-----
.../access/DataDomainRelationshipAction.java | 2 +-
.../apache/cayenne/access/ListWithPrefetches.java | 2 +-
.../org/apache/cayenne/access/ObjectStore.java | 2 +-
.../apache/cayenne/access/QueryResultItems.java | 71 +++++++
.../java/org/apache/cayenne/query/MappedExec.java | 15 +-
.../org/apache/cayenne/query/ProcedureCall.java | 2 +-
.../org/apache/cayenne/query/ProcedureQuery.java | 3 +-
.../org/apache/cayenne/query/QueryResultItems.java | 52 ------
.../java/org/apache/cayenne/query/SQLExec.java | 5 +-
.../org/apache/cayenne/util/GenericResponse.java | 205 ---------------------
.../java/org/apache/cayenne/util/ListResponse.java | 90 ---------
.../java/org/apache/cayenne/MockDataChannel.java | 31 ++--
.../access/DataContextDecoratedStackIT.java | 4 +-
.../cayenne/access/DataContextEJBQLDeleteIT.java | 15 +-
.../cayenne/access/DataContextEJBQLDeletePKIT.java | 6 +-
.../DataContextEJBQLNumericalFunctionalIT.java | 6 +-
.../access/DataContextEJBQLUpdateCompoundIT.java | 7 +-
.../cayenne/access/DataContextEJBQLUpdateIT.java | 31 ++--
.../access/DataContextProcedureQueryIT.java | 12 +-
.../apache/cayenne/access/DataDomainFiltersIT.java | 19 +-
.../runtime/DataDomainProviderIT.java | 7 +-
.../org/apache/cayenne/query/MappedQueryIT.java | 9 +-
.../apache/cayenne/util/GenericResponseTest.java | 72 --------
.../org/apache/cayenne/util/ListResponseTest.java | 67 -------
.../_cayenne-guide/part2/objectContext.adoc | 10 +-
41 files changed, 305 insertions(+), 848 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index c6760dc79..5d1a2ff67 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -29,6 +29,7 @@ CAY-3019 Skip noop conditions in generated SQL
CAY-3020 Remove serialization contracts
CAY-3021 Get rid of ObjectIdQuery
CAY-3023 Replace QueryResult with a List of redesigned QueryResultItems
+CAY-3024 Replace QueryResponse with List<QueryResultItem>
Bug Fixes:
diff --git a/UPGRADE.md b/UPGRADE.md
index 38fce38de..2cb0f6650 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -187,12 +187,16 @@ Expression caseWhenExp = caseWhen(
```
Stored procedure OUT parameters are no longer disguised as a one-row result set. They arrive as a dedicated
- `QueryResultItem.OutParameters` item (a map keyed by parameter name), `QueryResponse` reports them via
- `isOutParameters()` / `currentOutParameters()`.
+ `QueryResultItem.OutParameters` item (a map keyed by parameter name).
The cgen templates now emit `List<QueryResultItem>` instead of `QueryResult<?>` for the `perform*` methods of
mapped exec queries, so regenerate your classes via Modeler ("Tools" → "Generate Classes") or the AI plugin
- if you have generated classes with multi-part queries.
+ if you have generated classes with multipart queries.
+
+* Per [CAY-3024](https://issues.apache.org/jira/browse/CAY-3024) `org.apache.cayenne.QueryResponse` was removed. Everything that
+ used to return a `QueryResponse` - `ObjectContext.performGenericQuery(..)`, etc. - now return
+ `List<QueryResultItem>`. See CAY-3023 above for the example of how to process the result. Custom
+ `DataChannelQueryFilter` implementations now require a new signature.
* The `org.apache.cayenne.query.ParameterizedQuery` interface was removed, together with the `createQuery(Map)`
methods of `SQLTemplate`, `ProcedureQuery` and `ObjectSelect` that implemented it. Applying parameters to a mapped
@@ -218,8 +222,8 @@ Expression caseWhenExp = caseWhen(
query filters need to add the parameter and pass it down the chain:
```java
- public QueryResponse onQuery(ObjectContext context, Query query, boolean iteratedResult,
- DataChannelQueryFilterChain chain) {
+ public List<QueryResultItem> onQuery(ObjectContext context, Query query, boolean iteratedResult,
+ DataChannelQueryFilterChain chain) {
return chain.onQuery(context, query, iteratedResult);
}
```
diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
index 1c7196634..9ed861534 100644
--- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
+++ b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/StringIdQuery.java
@@ -48,8 +48,8 @@ import java.util.Map;
* A query that allows to fetch objects based on one or more String IDs. The returned
* objects do not have to be of the same type, be related via inheritance, or come from
* the same DB table. Note that if you expect multiple types of objects, use
- * {@link ObjectContext#performGenericQuery(Query)}. The returned QueryResponse will
- * contain separate lists of DataRows for each type in no particular order.
+ * {@link ObjectContext#performGenericQuery(Query)}. The returned result will contain a separate
+ * {@link org.apache.cayenne.QueryResultItem.Select} of DataRows for each type in no particular order.
* <p>
* As of this writing, a limitation of this query is that it returns DataRows that need to
* be manually converted to objects if needed.
diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/relationship/ObjectIdRelationshipFilter.java b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/relationship/ObjectIdRelationshipFilter.java
index dfa4ac1fb..700550275 100644
--- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/relationship/ObjectIdRelationshipFilter.java
+++ b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/relationship/ObjectIdRelationshipFilter.java
@@ -22,12 +22,14 @@ import org.apache.cayenne.DataChannelQueryFilter;
import org.apache.cayenne.DataChannelQueryFilterChain;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.annotation.PostLoad;
import org.apache.cayenne.annotation.PostPersist;
import org.apache.cayenne.annotation.PostUpdate;
import org.apache.cayenne.query.Query;
+import java.util.List;
+
/**
* A {@link DataChannelQueryFilter} that implements ObjectId relationships read functionality.
*
@@ -45,8 +47,8 @@ public class ObjectIdRelationshipFilter implements DataChannelQueryFilter {
return new ObjectIdRelationshipBatchFaultingStrategy();
}
- public QueryResponse onQuery(ObjectContext context, Query query, boolean iteratedResult,
- DataChannelQueryFilterChain chain) {
+ public List<QueryResultItem> onQuery(ObjectContext context, Query query, boolean iteratedResult,
+ DataChannelQueryFilterChain chain) {
try {
return chain.onQuery(context, query, iteratedResult);
} finally {
diff --git a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/id/StringIdQueryTest.java b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/id/StringIdQueryTest.java
index 3e9bc1561..865a5179f 100644
--- a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/id/StringIdQueryTest.java
+++ b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/id/StringIdQueryTest.java
@@ -19,7 +19,7 @@
package org.apache.cayenne.lifecycle.id;
import org.apache.cayenne.DataRow;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.configuration.DataNodeDescriptor;
import org.apache.cayenne.datasource.CayenneDataSource;
import org.apache.cayenne.runtime.CayenneRuntime;
@@ -96,16 +96,17 @@ public class StringIdQueryTest {
e1Helper.insert(3).insert(4);
StringIdQuery query = new StringIdQuery("E1:3", "E1:4", "E1:5");
- QueryResponse response = runtime.newContext().performGenericQuery(query);
+ List<QueryResultItem> response = runtime.newContext().performGenericQuery(query);
assertEquals(1, response.size());
- assertEquals(2, response.firstList().size());
+ List<?> rows = ((QueryResultItem.Select<?>) response.getFirst()).objects();
+ assertEquals(2, rows.size());
Set<Number> ids = new HashSet<>();
- DataRow r1 = (DataRow) response.firstList().get(0);
+ DataRow r1 = (DataRow) rows.get(0);
ids.add((Number) r1.get("ID"));
- DataRow r2 = (DataRow) response.firstList().get(1);
+ DataRow r2 = (DataRow) rows.get(1);
ids.add((Number) r2.get("ID"));
assertTrue(ids.contains(3L));
@@ -121,14 +122,14 @@ public class StringIdQueryTest {
e2Helper.insert(5).insert(6).insert(7);
StringIdQuery query = new StringIdQuery("E1:3", "E1:4", "E2:6", "E1:5");
- QueryResponse response = runtime.newContext().performGenericQuery(query);
+ List<QueryResultItem> response = runtime.newContext().performGenericQuery(query);
assertEquals(2, response.size());
Set<String> ids = new HashSet<>();
- while (response.next()) {
+ for (QueryResultItem item : response) {
@SuppressWarnings("unchecked")
- List<DataRow> list = (List<DataRow>) response.currentList();
+ List<DataRow> list = ((QueryResultItem.Select<DataRow>) item).objects();
for (DataRow row : list) {
ids.add(row.getEntityName() + ":" + row.get("ID"));
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/DataChannel.java b/cayenne/src/main/java/org/apache/cayenne/DataChannel.java
index d76e5f347..6d0e347be 100644
--- a/cayenne/src/main/java/org/apache/cayenne/DataChannel.java
+++ b/cayenne/src/main/java/org/apache/cayenne/DataChannel.java
@@ -113,13 +113,14 @@ public interface DataChannel {
*
* @param context an ObjectContext that originated the query, used to register result objects.
* @param query a query to execute.
- * @param iteratedResult if true, the result is returned as a {@link ResultIterator} accessible via
- * {@link QueryResponse#firstIterator()}, and the caller is responsible for closing
- * it. If false, the result is fully read into a list.
- * @return a generic response object that encapsulates result of the execution.
+ * @param iteratedResult if true, the result is returned as a {@link ResultIterator} wrapped in a
+ * {@link QueryResultItem.Iterator}, and the caller is responsible for closing it. If false,
+ * the result is fully read into a list.
+ * @return all result sets, update counts, iterators and OUT parameters of the execution, in the order they were
+ * produced.
* @since 5.0
*/
- QueryResponse onQuery(ObjectContext context, Query query, boolean iteratedResult);
+ List<QueryResultItem> onQuery(ObjectContext context, Query query, boolean iteratedResult);
/**
* Invalidates objects with the given ids in this channel and all its parents, so that they are refetched on the
diff --git a/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilter.java b/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilter.java
index 423f28963..3743f0cd5 100644
--- a/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilter.java
+++ b/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilter.java
@@ -21,17 +21,19 @@ package org.apache.cayenne;
import org.apache.cayenne.query.Query;
+import java.util.List;
+
/**
* An interface of a filter that allows to intercept DataChannel query operations.
* Query filters allow to implement chains of custom processors around a DataChannel.
* <p>
* Example: <pre>{@code
* public class MyQueryFilter implements DataChannelQueryFilter {
- * public QueryResponse onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
- * DataChannelQueryFilterChain filterChain) {
+ * public List<QueryResultItem> onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
+ * DataChannelQueryFilterChain filterChain) {
* System.out.println("Do something before query");
* // process query or return some custom response
- * QueryResponse response = filterChain.onQuery(originatingContext, query, iteratedResult);
+ * List<QueryResultItem> response = filterChain.onQuery(originatingContext, query, iteratedResult);
* System.out.println("Do something after query");
* return response;
* }
@@ -50,9 +52,9 @@ public interface DataChannelQueryFilter {
* @param query that is processed
* @param iteratedResult whether the result should be returned as a {@link ResultIterator} instead of a list
* @param filterChain chain of query filters to invoke after this filter
- * @return query response
+ * @return query result items
*/
- QueryResponse onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
- DataChannelQueryFilterChain filterChain);
+ List<QueryResultItem> onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
+ DataChannelQueryFilterChain filterChain);
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilterChain.java b/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilterChain.java
index 02d0cfdcd..c21409be2 100644
--- a/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilterChain.java
+++ b/cayenne/src/main/java/org/apache/cayenne/DataChannelQueryFilterChain.java
@@ -21,6 +21,8 @@ package org.apache.cayenne;
import org.apache.cayenne.query.Query;
+import java.util.List;
+
/**
* Interface for chain of query filters
*
@@ -28,6 +30,6 @@ import org.apache.cayenne.query.Query;
*/
public interface DataChannelQueryFilterChain {
- QueryResponse onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult);
+ List<QueryResultItem> onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult);
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java b/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java
index a7057861d..e0a25bd13 100644
--- a/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java
+++ b/cayenne/src/main/java/org/apache/cayenne/ObjectContext.java
@@ -349,10 +349,10 @@ public interface ObjectContext {
<T> ResultBatchIterator<T> batchIterator(Select<T> query, int size);
/**
- * Executes any kind of query providing the result in a form of
- * QueryResponse.
+ * Executes any kind of query, returning all of its result sets, update counts, iterators and OUT parameters as a
+ * list of {@link QueryResultItem}s in the order they were produced.
*/
- QueryResponse performGenericQuery(Query query);
+ List<QueryResultItem> performGenericQuery(Query query);
/**
* Returns GraphManager that manages object graph associated with this
diff --git a/cayenne/src/main/java/org/apache/cayenne/QueryResponse.java b/cayenne/src/main/java/org/apache/cayenne/QueryResponse.java
deleted file mode 100644
index 456b7a8ef..000000000
--- a/cayenne/src/main/java/org/apache/cayenne/QueryResponse.java
+++ /dev/null
@@ -1,165 +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;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Represents a result of query execution. It potentially contain a mix of update counts
- * and lists of selected values. Provides API somewhat similar to java.util.Iterator or
- * java.sql.ResultSet for scanning through the individual results.
- * <p>
- * An example of iterating through a response:
- * </p>
- *
- * <pre>
- * QueryResponse response = context.performGenericQuery(query);
- * for (response.reset(); response.next();) {
- * if (response.isList()) {
- * List list = response.currentList();
- * // ...
- * }
- * else {
- * int[] updateCounts = response.currentUpdateCount();
- * // ...
- * }
- * }
- * </pre>
- *
- * <p>
- * In case the structure of the result is known, and only a single list or an update count
- * is expected, there is a simpler API to access them:
- * </p>
- *
- * <pre>
- * QueryResponse response = context.performGenericQuery(query);
- * List list = response.firstList();
- * int[] count = response.firstUpdateCount();
- * </pre>
- *
- * @since 1.2
- */
-public interface QueryResponse {
-
- /**
- * Returns a number of results in the response.
- */
- int size();
-
- /**
- * Returns whether current iteration result is a list or an update count.
- */
- default boolean isList() {
- return false;
- }
-
- /**
- * Returns whether current response is an iterator
- *
- * @since 5.0
- */
- default boolean isIterator() {
- return false;
- }
-
- /**
- * Returns a List under the current iterator position. Use {@link #isList()} to check
- * the result type before calling this method.
- */
- default List<?> currentList() {
- return null;
- }
-
- /**
- * Returns a current iterator.
- *
- * @since 5.0
- */
- default ResultIterator<?> currentIterator() {
- return null;
- }
-
- /**
- * Returns an update count under the current iterator position. Returned value is an
- * int[] to accommodate batch queries. For a regular update result, the value will be
- * an int[1]. Use {@link #isList()} to check the result type before calling this
- * method.
- */
- default int[] currentUpdateCount() {
- return null;
- }
-
- /**
- * Returns whether the current iteration result is a map of stored procedure OUT parameters.
- *
- * @since 5.0
- */
- default boolean isOutParameters() {
- return false;
- }
-
- /**
- * Returns the current iteration result as a map of stored procedure OUT parameters keyed by name. Returns null
- * unless {@link #isOutParameters()} is true.
- *
- * @since 5.0
- */
- default Map<String, ?> currentOutParameters() {
- return null;
- }
-
- /**
- * Rewinds response iterator to the next result, returning true if it is available.
- */
- boolean next();
-
- /**
- * Restarts response iterator.
- */
- void reset();
-
- /**
- * A utility method for quickly retrieving the first list in the response. Returns
- * null if the query has no lists. Note that this method resets current iterator to an
- * undefined state.
- */
- default List<?> firstList() {
- return null;
- }
-
- /**
- * A utility method for quickly retrieving the Iterator in the response. Returns
- * null if the query has no iterator.
- *
- * @since 5.0
- */
- default ResultIterator<?> firstIterator() {
- return null;
- }
-
- /**
- * A utility method for quickly retrieving the first update count from the response.
- * Note that this method resets current iterator to an undefined state.
- */
- default int[] firstUpdateCount() {
- return null;
- }
-}
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
index 9f6117d98..9c25346bc 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -28,7 +28,7 @@ import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.PersistenceState;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.ResultBatchIterator;
import org.apache.cayenne.ResultIterator;
import org.apache.cayenne.ResultIteratorCallback;
@@ -57,7 +57,6 @@ import org.apache.cayenne.reflect.PropertyVisitor;
import org.apache.cayenne.reflect.ToManyProperty;
import org.apache.cayenne.reflect.ToOneProperty;
import org.apache.cayenne.util.EventUtil;
-import org.apache.cayenne.util.GenericResponse;
import org.apache.cayenne.util.ShallowMergeOperation;
import org.apache.cayenne.util.Util;
@@ -1018,28 +1017,28 @@ public class DataContext implements ObjectContext {
@SuppressWarnings("unchecked")
public <T> ResultIterator<T> iterator(Select<T> query) {
Query queryToRun = nonNullDelegate().willPerformQuery(this, query);
- QueryResponse queryResponse = onQuery(queryToRun, true, false);
- return (ResultIterator<T>) queryResponse.firstIterator();
+ return (ResultIterator<T>) QueryResultItems.firstIterator(onQuery(queryToRun, true, false));
}
/**
- * Executes a query returning a generic response.
+ * Executes a query returning all of its result sets, update counts, iterators and OUT parameters in the order
+ * they were produced.
*
* @since 1.2
*/
@Override
- public QueryResponse performGenericQuery(Query query) {
+ public List<QueryResultItem> performGenericQuery(Query query) {
query = nonNullDelegate().willPerformGenericQuery(this, query);
if (query == null) {
- return new GenericResponse();
+ return Collections.emptyList();
}
if (this.getChannel() == null) {
throw new CayenneRuntimeException("Can't run query - parent DataChannel is not set.");
}
- return onQuery(query, false, false);
+ return Collections.unmodifiableList(onQuery(query, false, false));
}
/**
@@ -1069,11 +1068,11 @@ public class DataContext implements ObjectContext {
return new ArrayList<>(1);
}
- List<?> result = onQuery(query, false, false).firstList();
+ List<?> result = QueryResultItems.firstList(onQuery(query, false, false));
return result != null ? result : new ArrayList<>(1);
}
- QueryResponse onQuery(Query query, boolean iteratedResult, boolean ignoreLocalCache) {
+ List<QueryResultItem> onQuery(Query query, boolean iteratedResult, boolean ignoreLocalCache) {
return new DataContextQueryAction(this, query, iteratedResult, ignoreLocalCache).execute();
}
@@ -1084,7 +1083,7 @@ public class DataContext implements ObjectContext {
* @since 1.1
*/
public int[] performNonSelectingQuery(Query query) {
- int[] count = performGenericQuery(query).firstUpdateCount();
+ int[] count = QueryResultItems.firstUpdateCount(performGenericQuery(query));
return count != null ? count : new int[0];
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContextChannel.java b/cayenne/src/main/java/org/apache/cayenne/access/DataContextChannel.java
index 5226093a0..c6b8529b9 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContextChannel.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContextChannel.java
@@ -24,7 +24,7 @@ import org.apache.cayenne.DataChannel;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.event.EventManager;
import org.apache.cayenne.graph.ChildDiffLoader;
import org.apache.cayenne.graph.CompoundDiff;
@@ -33,7 +33,6 @@ import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.EntityResultSegment;
import org.apache.cayenne.query.Query;
import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.util.GenericResponse;
import org.apache.cayenne.util.ShallowMergeOperation;
import java.util.ArrayList;
@@ -70,10 +69,10 @@ public record DataContextChannel(DataContext context) implements DataChannel {
}
@Override
- public QueryResponse onQuery(ObjectContext childContext, Query query, boolean iteratedResult) {
+ public List<QueryResultItem> onQuery(ObjectContext childContext, Query query, boolean iteratedResult) {
checkChildContext(childContext);
- QueryResponse response = context.onQuery(query, iteratedResult, true);
+ List<QueryResultItem> response = context.onQuery(query, iteratedResult, true);
QueryMetadata metadata = query.getMetaData(getEntityResolver());
return metadata.isFetchingDataRows()
@@ -152,21 +151,21 @@ public record DataContextChannel(DataContext context) implements DataChannel {
}
}
- private QueryResponse transferObjectsToChildContext(
+ private List<QueryResultItem> transferObjectsToChildContext(
ObjectContext childContext,
- QueryResponse response,
+ List<QueryResultItem> response,
QueryMetadata metadata) {
// rewrite response to contain objects from the query context
- GenericResponse childResponse = new GenericResponse();
+ List<QueryResultItem> childResponse = new ArrayList<>(response.size());
ShallowMergeOperation merger = null;
- for (response.reset(); response.next(); ) {
- if (response.isList()) {
- List<?> objects = response.currentList();
+ for (QueryResultItem item : response) {
+ if (item instanceof QueryResultItem.Select<?> select) {
+ List<?> objects = select.objects();
if (objects.isEmpty()) {
- childResponse.addResultList(objects);
+ childResponse.add(item);
} else {
// minor optimization, skip Object[] if there are no persistent objects
@@ -206,10 +205,10 @@ public record DataContextChannel(DataContext context) implements DataChannel {
}
}
- childResponse.addResultList(childObjects);
+ childResponse.add(new QueryResultItem.Select<>(childObjects));
}
} else {
- childResponse.addBatchUpdateCount(response.currentUpdateCount());
+ childResponse.add(item);
}
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java b/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
index 749903d8e..33c2fc37a 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
@@ -19,7 +19,7 @@
package org.apache.cayenne.access;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.cache.QueryCache;
import org.apache.cayenne.cache.QueryCacheEntryFactory;
import org.apache.cayenne.map.DbEntity;
@@ -27,7 +27,6 @@ import org.apache.cayenne.query.EntityResultSegment;
import org.apache.cayenne.query.Query;
import org.apache.cayenne.query.QueryCacheStrategy;
import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.util.ListResponse;
import java.util.Collections;
import java.util.List;
@@ -42,7 +41,7 @@ class DataContextQueryAction {
private final QueryMetadata metadata;
private final boolean iteratedResult;
private final boolean ignoreLocalCache;
- private QueryResponse response;
+ private List<QueryResultItem> response;
public DataContextQueryAction(DataContext context, Query query, boolean iteratedResult, boolean ignoreLocalCache) {
this.context = context;
@@ -55,7 +54,7 @@ class DataContextQueryAction {
/**
* Worker method that performs internal query.
*/
- public QueryResponse execute() {
+ public List<QueryResultItem> execute() {
if (interceptIteratedQuery() != DONE) {
if (interceptLocalCache() != DONE) {
executePostCache();
@@ -85,12 +84,12 @@ class DataContextQueryAction {
// this will select raw ids
runQuery();
- List<?> rawIds = response.firstList();
+ List<?> rawIds = QueryResultItems.firstList(response);
int maxIdQualifierSize = context.getChannel().getDataDomain().getMaxIdQualifierSize();
IncrementalFaultList<?> paginatedList = createIncrementalFaultList(rawIds, maxIdQualifierSize);
// replace result with a paginated list that will deal with id-to-object resolution
- response = new ListResponse(paginatedList);
+ response = List.of(new QueryResultItem.Select<>(paginatedList));
return DONE;
}
@@ -153,12 +152,13 @@ class DataContextQueryAction {
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
+ // the cache may hold no list at all if the query produced no select result
if (response == null || wasResponseNull) {
- response = new ListResponse(cachedResults);
+ response = cachedResults != null ? List.of(new QueryResultItem.Select<>(cachedResults)) : List.of();
}
} else {
// on cache-refresh request, fetch without blocking and fill the cache
@@ -175,7 +175,7 @@ class DataContextQueryAction {
protected QueryCacheEntryFactory getCacheObjectFactory() {
return () -> {
executePostCache();
- List result = response.firstList();
+ List<?> result = QueryResultItems.firstList(response);
// make an immutable list to make sure callers don't mess it up
return result != null ? Collections.unmodifiableList(result) : null;
};
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
index a4b6cd513..b5c6b9227 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomain.java
@@ -28,7 +28,7 @@ import org.apache.cayenne.DataChannelSyncFilterChain;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.access.flush.DataDomainFlushAction;
import org.apache.cayenne.access.flush.DataDomainFlushActionFactory;
import org.apache.cayenne.cache.QueryCache;
@@ -344,12 +344,12 @@ public class DataDomain implements DataChannel {
}
/**
- * Runs query returning generic QueryResponse.
+ * Runs a query, returning all of its results as a list of {@link QueryResultItem}s.
*
* @since 1.2
*/
@Override
- public QueryResponse onQuery(ObjectContext context, Query query, boolean iteratedResult) {
+ public List<QueryResultItem> onQuery(ObjectContext context, Query query, boolean iteratedResult) {
checkStopped();
return new DataDomainQueryFilterChain().onQuery(context, query, iteratedResult);
}
@@ -405,7 +405,7 @@ public class DataDomain implements DataChannel {
return new DataDomainRelationshipAction(this, context, sourceId, relationshipName).execute();
}
- QueryResponse onQueryNoFilters(ObjectContext originatingContext, Query query, boolean iteratedResult) {
+ List<QueryResultItem> onQueryNoFilters(ObjectContext originatingContext, Query query, boolean iteratedResult) {
// transaction note:
// we don't wrap this code in transaction to reduce transaction scope to
// just the DB operation for better performance ... query action will
@@ -573,7 +573,7 @@ public class DataDomain implements DataChannel {
}
@Override
- public QueryResponse onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult) {
+ public List<QueryResultItem> onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult) {
return --idx >= 0
? queryFilters.get(idx).onQuery(originatingContext, query, iteratedResult, this)
: onQueryNoFilters(originatingContext, query, iteratedResult);
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainIdQueryAction.java b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainIdQueryAction.java
index bb14bb49d..01c05e2f4 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainIdQueryAction.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainIdQueryAction.java
@@ -95,7 +95,7 @@ class DataDomainIdQueryAction {
.query(Persistent.class, id.getEntityName())
.where(ExpressionFactory.matchAllDbExp(id.getIdSnapshot(), Expression.EQUAL_TO));
- List<?> objects = domain.onQuery(context, query, false).firstList();
+ List<?> objects = QueryResultItems.firstList(domain.onQuery(context, query, false));
return switch (objects.size()) {
case 0 -> null;
case 1 -> (Persistent) objects.getFirst();
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
index c618ea410..0129d5ee4 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
@@ -26,7 +26,7 @@ import org.apache.cayenne.EmbeddableObject;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.ResultIterator;
import org.apache.cayenne.access.translator.TranslatedStatement;
import org.apache.cayenne.cache.QueryCache;
@@ -51,8 +51,6 @@ import org.apache.cayenne.reflect.LifecycleCallbackRegistry;
import org.apache.cayenne.tx.BaseTransaction;
import org.apache.cayenne.tx.ReadOnlyTransaction;
import org.apache.cayenne.tx.Transaction;
-import org.apache.cayenne.util.GenericResponse;
-import org.apache.cayenne.util.ListResponse;
import org.apache.cayenne.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,7 +63,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
@@ -88,8 +85,8 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
private final QueryMetadata metadata;
private final AdhocObjectFactory objectFactory;
- private QueryResponse response;
- private GenericResponse fullResponse;
+ private List<QueryResultItem> response;
+ private List<QueryResultItem> fullResponse;
private final boolean iteratedResult;
private boolean iteratorExclusiveConnection;
private Map<CayennePath, List<?>> prefetchResultsByPath;
@@ -105,7 +102,7 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
/*
* A constructor for the "new" way of performing a query via 'execute' with
- * QueryResponse created internally.
+ * the result list created internally.
*/
DataDomainQueryAction(ObjectContext context, DataDomain domain, Query query, boolean iteratedResult) {
if (context != null && !(context instanceof DataContext)) {
@@ -121,7 +118,7 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
this.objectFactory = domain.getObjectFactory();
}
- QueryResponse execute() {
+ List<QueryResultItem> execute() {
// run chain...
if (interceptIteratedQuery() != DONE) {
@@ -165,7 +162,7 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
// The transaction is owned by the caller, and is not aligned with the iterator scope
if (BaseTransaction.getThreadTransaction() != null) {
runQuery();
- Objects.requireNonNull(fullResponse.firstIterator(), "Iterator response expected");
+ firstIteratorIndex();
}
// The transaction will be owned by the iterator, and will outlive the query action
@@ -177,8 +174,9 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
// connection
iteratorExclusiveConnection = true;
runQuery();
- ResultIterator<?> it = Objects.requireNonNull(fullResponse.firstIterator(), "Iterator response expected");
- fullResponse.replaceResult(it, new TransactionResultIteratorDecorator<>(it, tx));
+ int index = firstIteratorIndex();
+ ResultIterator<?> it = ((QueryResultItem.Iterator<?>) fullResponse.get(index)).iterator();
+ fullResponse.set(index, new QueryResultItem.Iterator<>(new TransactionResultIteratorDecorator<>(it, tx)));
} catch (Throwable th) {
// No iterator will be returned to the caller, so nothing will ever commit this transaction. Mark it
// for rollback below, so that its connections are not leaked. Note that a DataNode marks the
@@ -195,8 +193,15 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
}
}
}
+ }
- fullResponse.reset();
+ private int firstIteratorIndex() {
+ for (int i = 0; i < fullResponse.size(); i++) {
+ if (fullResponse.get(i) instanceof QueryResultItem.Iterator) {
+ return i;
+ }
+ }
+ throw new CayenneRuntimeException("Iterator response expected");
}
/*
@@ -226,8 +231,12 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
// it is null if there was a preexisting cache entry
cacheHit = (response == null);
+ // the cache may hold no list at all if the query produced no select result
if (response == null || wasResponseNull) {
- response = new ListResponse(cachedResults);
+ response = new ArrayList<>(1);
+ if (cachedResults != null) {
+ response.add(new QueryResultItem.Select<>(cachedResults));
+ }
}
// Mark as cached result - lists need copying whether hit or miss
@@ -250,7 +259,7 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
return () -> {
runQueryInTransaction();
- List<?> list = response.firstList();
+ List<?> list = QueryResultItems.firstList(response);
if (list != null) {
// make an immutable list to make sure callers don't mess it up
@@ -285,7 +294,7 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
private void runQuery() {
// reset
- this.fullResponse = new GenericResponse();
+ this.fullResponse = new ArrayList<>();
this.response = this.fullResponse;
this.queriesByNode = null;
@@ -313,24 +322,23 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
}
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()) {
- List<?> result = converter.convert((List) mainRows);
- if (result != mainRows) {
- updateResponse(mainRows, result);
+ for (int i = 0; i < response.size(); i++) {
+ switch (response.get(i)) {
+ case QueryResultItem.Select<?> select -> {
+ List<?> mainRows = select.objects(); // List<DataRow> or List<Object[]>
+ if (!mainRows.isEmpty()) {
+ List<?> result = converter.convert((List) mainRows);
+ if (result != mainRows) {
+ response.set(i, new QueryResultItem.Select<>(result));
+ }
}
}
- } else if (response.isIterator()) {
- // iterator should be a part of full response
- ResultIterator<?> iterator = fullResponse.currentIterator();
- fullResponse.replaceResult(iterator, new ResultIteratorConverterDecorator(iterator, converter));
+ case QueryResultItem.Iterator<?> iterator -> response.set(i, new QueryResultItem.Iterator<>(
+ new ResultIteratorConverterDecorator(iterator.iterator(), converter)));
+ default -> {
+ }
}
}
- response.reset();
}
private boolean noObjectConversion() {
@@ -424,12 +432,14 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
@Override
public void nextCount(Query query, int resultCount) {
- fullResponse.addUpdateCount(resultCount);
+ fullResponse.add(new QueryResultItem.Update(new int[]{resultCount}));
}
@Override
public void nextBatchCount(Query query, int[] resultCount) {
- fullResponse.addBatchUpdateCount(resultCount);
+ if (resultCount != null) {
+ fullResponse.add(new QueryResultItem.Update(resultCount));
+ }
}
@Override
@@ -438,7 +448,7 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
if (prefetchResultsByPath != null && query instanceof PrefetchSelectQuery<?> prefetchQuery) {
prefetchResultsByPath.put(prefetchQuery.getPrefetchPath(), dataRows);
} else {
- fullResponse.addResultList(dataRows);
+ fullResponse.add(new QueryResultItem.Select<>(dataRows));
}
}
@@ -448,13 +458,13 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
if (prefetchResultsByPath != null && query instanceof PrefetchSelectQuery<?> prefetchQuery) {
prefetchResultsByPath.put(prefetchQuery.getPrefetchPath(), (List<?>) it);
} else {
- this.fullResponse.addResultIterator(it);
+ fullResponse.add(new QueryResultItem.Iterator<>(it));
}
}
@Override
public void nextOutParameters(Query query, Map<String, ?> outParameters) {
- fullResponse.addOutParameters(outParameters);
+ fullResponse.add(new QueryResultItem.OutParameters(outParameters));
}
@Override
@@ -496,16 +506,6 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
return iteratorExclusiveConnection;
}
- protected <T, R> void updateResponse(List<T> sourceObjects, List<? extends R> targetObjects) {
- if (response instanceof GenericResponse genericResponse) {
- genericResponse.replaceResult(sourceObjects, targetObjects);
- } else if (response instanceof ListResponse) {
- response = new ListResponse(targetObjects);
- } else {
- throw new IllegalStateException("Unknown response object: " + response);
- }
- }
-
abstract class ObjectConversionStrategy<T, R> {
abstract List<? extends R> convert(List<T> mainRows);
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainRelationshipAction.java b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainRelationshipAction.java
index cb1dffd0b..a7860c686 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainRelationshipAction.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainRelationshipAction.java
@@ -148,7 +148,7 @@ class DataDomainRelationshipAction {
.where(qualifier);
@SuppressWarnings("unchecked")
- List<Persistent> related = (List<Persistent>) domain.onQuery(context, query, false).firstList();
+ List<Persistent> related = (List<Persistent>) QueryResultItems.firstList(domain.onQuery(context, query, false));
return related;
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ListWithPrefetches.java b/cayenne/src/main/java/org/apache/cayenne/access/ListWithPrefetches.java
index ee2c8c4cf..c2931b76d 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/ListWithPrefetches.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/ListWithPrefetches.java
@@ -37,7 +37,7 @@ import java.util.Map;
// of changing DataRowStore result caching API. Since we are doing it when already in 1.2
// beta, I am choosing the least invasive way that doesn't affect public API.
//
-// Future alternatives may include caching the entire QueryResponse... or maybe leaving
+// Future alternatives may include caching the entire query result... or maybe leaving
// everything the way it is.
class ListWithPrefetches implements List<Object> {
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java
index 3155f6ee1..5fa119854 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java
@@ -495,7 +495,7 @@ public class ObjectStore implements SnapshotEventListener, GraphManager {
.where(ExpressionFactory.matchAllDbExp(oid.getIdSnapshot(), Expression.EQUAL_TO))
.fetchDataRows();
- List<?> rows = context.getChannel().onQuery(context, query, false).firstList();
+ List<?> rows = QueryResultItems.firstList(context.getChannel().onQuery(context, query, false));
return switch (rows.size()) {
case 0 -> null;
case 1 -> (DataRow) rows.getFirst();
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/QueryResultItems.java b/cayenne/src/main/java/org/apache/cayenne/access/QueryResultItems.java
new file mode 100644
index 000000000..bd25d855f
--- /dev/null
+++ b/cayenne/src/main/java/org/apache/cayenne/access/QueryResultItems.java
@@ -0,0 +1,71 @@
+/*****************************************************************
+ * 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.access;
+
+import org.apache.cayenne.QueryResultItem;
+import org.apache.cayenne.ResultIterator;
+
+import java.util.List;
+
+/**
+ * Lookups of the first item of a given kind in a multipart query result.
+ *
+ * @since 5.0
+ */
+final class QueryResultItems {
+
+ private QueryResultItems() {
+ }
+
+ /**
+ * Returns the list of the first {@link QueryResultItem.Select} item, or null if there is none.
+ */
+ static List<?> firstList(List<QueryResultItem> items) {
+ for (QueryResultItem item : items) {
+ if (item instanceof QueryResultItem.Select<?> select) {
+ return select.objects();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the iterator of the first {@link QueryResultItem.Iterator} item, or null if there is none.
+ */
+ static ResultIterator<?> firstIterator(List<QueryResultItem> items) {
+ for (QueryResultItem item : items) {
+ if (item instanceof QueryResultItem.Iterator<?> iterator) {
+ return iterator.iterator();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the counts of the first {@link QueryResultItem.Update} item, or null if there is none.
+ */
+ static int[] firstUpdateCount(List<QueryResultItem> items) {
+ for (QueryResultItem item : items) {
+ if (item instanceof QueryResultItem.Update update) {
+ return update.counts();
+ }
+ }
+ return null;
+ }
+}
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/MappedExec.java b/cayenne/src/main/java/org/apache/cayenne/query/MappedExec.java
index 57c1591e6..44e4ecce0 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/MappedExec.java
+++ b/cayenne/src/main/java/org/apache/cayenne/query/MappedExec.java
@@ -18,6 +18,7 @@
****************************************************************/
package org.apache.cayenne.query;
+import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.QueryResultItem;
@@ -56,10 +57,20 @@ public class MappedExec extends AbstractMappedQuery {
* were produced.
*/
public List<QueryResultItem> execute(ObjectContext context) {
- return QueryResultItems.fromResponse(context.performGenericQuery(this));
+ return context.performGenericQuery(this);
}
+ /**
+ * Executes the query, returning the counts of its first update result.
+ *
+ * @throws CayenneRuntimeException if the query produced no update results.
+ */
public int[] update(ObjectContext context) {
- return context.performGenericQuery(this).firstUpdateCount();
+ for (QueryResultItem item : execute(context)) {
+ if (item instanceof QueryResultItem.Update update) {
+ return update.counts();
+ }
+ }
+ throw new CayenneRuntimeException("Expected an update result from query '%s'", queryName);
}
}
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/ProcedureCall.java b/cayenne/src/main/java/org/apache/cayenne/query/ProcedureCall.java
index dbb8c0e1b..6e66768e0 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/ProcedureCall.java
+++ b/cayenne/src/main/java/org/apache/cayenne/query/ProcedureCall.java
@@ -134,7 +134,7 @@ public class ProcedureCall<T> extends IndirectQuery {
* were produced.
*/
public List<QueryResultItem> call(ObjectContext context) {
- return QueryResultItems.fromResponse(context.performGenericQuery(this));
+ return context.performGenericQuery(this);
}
/**
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/ProcedureQuery.java b/cayenne/src/main/java/org/apache/cayenne/query/ProcedureQuery.java
index d9e961097..5bd4ade59 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/ProcedureQuery.java
+++ b/cayenne/src/main/java/org/apache/cayenne/query/ProcedureQuery.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.query;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.EntityResolver;
@@ -41,7 +42,7 @@ import java.util.Map;
* <h4>Reading OUT parameters</h4>
* <p>
* If a ProcedureQuery has OUT parameters, their values are reported as a separate item of the query response, a Map
- * keyed by parameter name (see {@link org.apache.cayenne.QueryResponse#currentOutParameters()}).
+ * keyed by parameter name (see {@link QueryResultItem.OutParameters}).
* </p>
* <h4>Using ProcedureQuery as a GenericSelectQuery</h4>
* <p>
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/QueryResultItems.java b/cayenne/src/main/java/org/apache/cayenne/query/QueryResultItems.java
deleted file mode 100644
index b75f98ec0..000000000
--- a/cayenne/src/main/java/org/apache/cayenne/query/QueryResultItems.java
+++ /dev/null
@@ -1,52 +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.query;
-
-import org.apache.cayenne.QueryResponse;
-import org.apache.cayenne.QueryResultItem;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Converts a {@link QueryResponse} into a list of {@link QueryResultItem}s.
- *
- * @since 5.0
- */
-final class QueryResultItems {
-
- private QueryResultItems() {
- }
-
- static List<QueryResultItem> fromResponse(QueryResponse response) {
- List<QueryResultItem> items = new ArrayList<>(response.size());
- for (response.reset(); response.next(); ) {
- if (response.isList()) {
- items.add(new QueryResultItem.Select<>(response.currentList()));
- } else if (response.isIterator()) {
- items.add(new QueryResultItem.Iterator<>(response.currentIterator()));
- } else if (response.isOutParameters()) {
- items.add(new QueryResultItem.OutParameters(response.currentOutParameters()));
- } else {
- items.add(new QueryResultItem.Update(response.currentUpdateCount()));
- }
- }
- return items;
- }
-}
diff --git a/cayenne/src/main/java/org/apache/cayenne/query/SQLExec.java b/cayenne/src/main/java/org/apache/cayenne/query/SQLExec.java
index 62d0a4730..e426d8b25 100644
--- a/cayenne/src/main/java/org/apache/cayenne/query/SQLExec.java
+++ b/cayenne/src/main/java/org/apache/cayenne/query/SQLExec.java
@@ -157,7 +157,7 @@ public class SQLExec extends IndirectQuery {
* Executes the query, returning all of its result sets and update counts in the order they were produced.
*/
public List<QueryResultItem> execute(ObjectContext context) {
- return QueryResultItems.fromResponse(context.performGenericQuery(this));
+ return context.performGenericQuery(this);
}
public int update(ObjectContext context) {
@@ -192,7 +192,8 @@ public class SQLExec extends IndirectQuery {
/**
* Flag indicating that generated keys should be returned by this query execution.
- * Generated keys could be read via {@link QueryResponse#currentList()} method
+ * Generated keys are returned as a {@link QueryResultItem.Select} item of {@link #execute(ObjectContext)}
+ * result.
*
* @param returnGeneratedKeys flag value
* @see java.sql.Statement#RETURN_GENERATED_KEYS
diff --git a/cayenne/src/main/java/org/apache/cayenne/util/GenericResponse.java b/cayenne/src/main/java/org/apache/cayenne/util/GenericResponse.java
deleted file mode 100644
index 6fc721f77..000000000
--- a/cayenne/src/main/java/org/apache/cayenne/util/GenericResponse.java
+++ /dev/null
@@ -1,205 +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 java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.cayenne.QueryResponse;
-import org.apache.cayenne.ResultIterator;
-
-/**
- * A simple implementation of QueryResponse.
- *
- * @since 1.2
- */
-public class GenericResponse implements QueryResponse {
-
- protected List<Object> results;
-
- protected int currentIndex;
-
- /**
- * Creates an empty BaseResponse.
- */
- public GenericResponse() {
- results = new ArrayList<>();
- }
-
- /**
- * Creates a BaseResponse with a single result list.
- */
- public GenericResponse(List<?> list) {
- results = new ArrayList<>(1);
- addResultList(list);
- }
-
- /**
- * Creates a response that it a shallow copy of another response.
- */
- @SuppressWarnings("unused")
- public GenericResponse(QueryResponse response) {
- results = new ArrayList<>(response.size());
-
- response.reset();
- while (response.next()) {
- if (response.isList()) {
- addResultList(response.currentList());
- } else if (response.isIterator()) {
- addResultIterator(response.currentIterator());
- } else if (response.isOutParameters()) {
- addOutParameters(response.currentOutParameters());
- } else {
- addBatchUpdateCount(response.currentUpdateCount());
- }
- }
- }
-
- @Override
- public List<?> firstList() {
- for (reset(); next(); ) {
- if (isList()) {
- return currentList();
- }
- }
-
- return null;
- }
-
- @Override
- public ResultIterator<?> firstIterator() {
- for (reset(); next(); ) {
- if (isIterator()) {
- return currentIterator();
- }
- }
-
- return null;
- }
-
- @Override
- public int[] firstUpdateCount() {
- for (reset(); next(); ) {
- if (results.get(currentIndex - 1) instanceof int[] counts) {
- return counts;
- }
- }
-
- return null;
- }
-
- @Override
- public List<?> currentList() {
- return (List<?>) results.get(currentIndex - 1);
- }
-
- @Override
- public ResultIterator<?> currentIterator() {
- return (ResultIterator<?>) results.get(currentIndex - 1);
- }
-
- @Override
- public int[] currentUpdateCount() {
- return (int[]) results.get(currentIndex - 1);
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public Map<String, ?> currentOutParameters() {
- return (Map<String, ?>) results.get(currentIndex - 1);
- }
-
- @Override
- public boolean isList() {
- return results.get(currentIndex - 1) instanceof List;
- }
-
- @Override
- public boolean isOutParameters() {
- return results.get(currentIndex - 1) instanceof Map;
- }
-
- @Override
- public boolean isIterator() {
- return results.get(currentIndex - 1) instanceof ResultIterator;
- }
-
- @Override
- public boolean next() {
- return ++currentIndex <= results.size();
- }
-
- @Override
- public void reset() {
- currentIndex = 0;
- }
-
- @Override
- public int size() {
- return results.size();
- }
-
- /**
- * Clears any previously collected information.
- */
- public void clear() {
- results.clear();
- }
-
- public void addBatchUpdateCount(int[] resultCount) {
- if (resultCount != null) {
- results.add(resultCount);
- }
- }
-
- public void addUpdateCount(int resultCount) {
- results.add(new int[]{resultCount});
- }
-
- public void addResultList(List<?> list) {
- results.add(list);
- }
-
- /**
- * @param iterator to add as a result
- * @since 5.0
- */
- public void addResultIterator(ResultIterator<?> iterator) {
- results.add(iterator);
- }
-
- /**
- * @since 5.0
- */
- public void addOutParameters(Map<String, ?> outParameters) {
- results.add(outParameters);
- }
-
- /**
- * Replaces previously stored result with a new result.
- */
- public void replaceResult(Object oldResult, Object newResult) {
- int index = results.indexOf(oldResult);
- if (index >= 0) {
- results.set(index, newResult);
- }
- }
-}
diff --git a/cayenne/src/main/java/org/apache/cayenne/util/ListResponse.java b/cayenne/src/main/java/org/apache/cayenne/util/ListResponse.java
deleted file mode 100644
index cd6e0a23b..000000000
--- a/cayenne/src/main/java/org/apache/cayenne/util/ListResponse.java
+++ /dev/null
@@ -1,90 +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 java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.cayenne.QueryResponse;
-
-/**
- * A QueryResponse optimized to hold a single object or data row list.
- *
- * @since 1.2
- */
-public class ListResponse implements QueryResponse {
-
- protected List<?> objectList;
-
- protected int currentIndex;
-
- /**
- * Creates an empty response.
- */
- public ListResponse() {
- this.objectList = new ArrayList<>(1);
- }
-
- public ListResponse(Object object) {
- this.objectList = Collections.singletonList(object);
- }
-
- public ListResponse(List<?> objectList) {
- this.objectList = objectList;
- }
-
- public int size() {
- return 1;
- }
-
- public boolean isList() {
- if (currentIndex != 1) {
- throw new IndexOutOfBoundsException("Past iteration end: " + currentIndex);
- }
-
- return true;
- }
-
- public List<?> currentList() {
- if (currentIndex != 1) {
- throw new IndexOutOfBoundsException("Past iteration end: " + currentIndex);
- }
-
- return objectList;
- }
-
- public int[] currentUpdateCount() {
- throw new IllegalStateException("Current object is not an update count");
- }
-
- public boolean next() {
- return ++currentIndex <= 1;
- }
-
- public void reset() {
- currentIndex = 0;
- }
-
- public List<?> firstList() {
- return objectList;
- }
-
-}
diff --git a/cayenne/src/test/java/org/apache/cayenne/MockDataChannel.java b/cayenne/src/test/java/org/apache/cayenne/MockDataChannel.java
index 168117d81..4d77020d0 100644
--- a/cayenne/src/test/java/org/apache/cayenne/MockDataChannel.java
+++ b/cayenne/src/test/java/org/apache/cayenne/MockDataChannel.java
@@ -23,14 +23,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import org.apache.cayenne.DataChannel;
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.QueryResponse;
import org.apache.cayenne.event.EventManager;
import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.query.Query;
-import org.apache.cayenne.util.GenericResponse;
/**
* Stores all messages passed via this handler.
@@ -41,7 +37,7 @@ public class MockDataChannel implements DataChannel {
protected EntityResolver resolver;
protected List requestObjects = new ArrayList();
protected GraphDiff commitResponse;
- protected QueryResponse response;
+ protected List<QueryResultItem> response;
public MockDataChannel() {
@@ -52,7 +48,7 @@ public class MockDataChannel implements DataChannel {
}
public MockDataChannel(List selectResponse) {
- this.response = new GenericResponse(selectResponse);
+ this.response = List.of(new QueryResultItem.Select<>(selectResponse));
}
public MockDataChannel(EntityResolver entityResolver, List selectResponse) {
@@ -60,13 +56,9 @@ public class MockDataChannel implements DataChannel {
this.resolver = entityResolver;
}
- public MockDataChannel(EntityResolver entityResolver, QueryResponse response) {
- this.resolver = entityResolver;
- this.response = response;
- }
-
public MockDataChannel(EntityResolver resolver) {
- this(resolver, new GenericResponse());
+ this.resolver = resolver;
+ this.response = new ArrayList<>();
}
public EventManager getEventManager() {
@@ -90,7 +82,7 @@ public class MockDataChannel implements DataChannel {
return commitResponse;
}
- public QueryResponse onQuery(ObjectContext context, Query query, boolean iteratedResult) {
+ public List<QueryResultItem> onQuery(ObjectContext context, Query query, boolean iteratedResult) {
requestObjects.add(query);
return response;
}
@@ -101,14 +93,23 @@ public class MockDataChannel implements DataChannel {
public Persistent onIdQuery(ObjectContext context, ObjectId id) {
requestObjects.add(id);
- List<?> objects = response != null ? response.firstList() : null;
+ List<?> objects = response != null ? firstList() : null;
return objects == null || objects.isEmpty() ? null : (Persistent) objects.getFirst();
}
public List<Persistent> onRelationshipQuery(ObjectContext context, ObjectId sourceId,
String relationshipName) {
requestObjects.add(sourceId);
- return (List<Persistent>) response.firstList();
+ return (List<Persistent>) firstList();
+ }
+
+ private List<?> firstList() {
+ for (QueryResultItem item : response) {
+ if (item instanceof QueryResultItem.Select<?> select) {
+ return select.objects();
+ }
+ }
+ return null;
}
public EntityResolver getEntityResolver() {
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextDecoratedStackIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextDecoratedStackIT.java
index 199dbad90..48f051742 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextDecoratedStackIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextDecoratedStackIT.java
@@ -23,7 +23,7 @@ import org.apache.cayenne.DataChannel;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.Persistent;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.dba.frontbase.FrontBaseAdapter;
import org.apache.cayenne.dba.mysql.MySQLAdapter;
import org.apache.cayenne.event.EventManager;
@@ -114,7 +114,7 @@ public class DataContextDecoratedStackIT {
}
@Override
- public QueryResponse onQuery(ObjectContext context, Query query, boolean iteratedResult) {
+ public List<QueryResultItem> onQuery(ObjectContext context, Query query, boolean iteratedResult) {
return channel.onQuery(context, query, iteratedResult);
}
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeleteIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeleteIT.java
index 299a927c4..2b3f81829 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeleteIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeleteIT.java
@@ -20,7 +20,7 @@ package org.apache.cayenne.access;
import org.apache.cayenne.Cayenne;
import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.runtime.CayenneRuntime;
import org.apache.cayenne.test.jdbc.TableHelper;
@@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import java.sql.Types;
+import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -72,9 +73,9 @@ public class DataContextEJBQLDeleteIT {
String ejbql = "delete from Painting";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
@@ -87,9 +88,9 @@ public class DataContextEJBQLDeleteIT {
String ejbql = "delete from Painting AS p";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
@@ -102,9 +103,9 @@ public class DataContextEJBQLDeleteIT {
String ejbql = "delete from Painting AS p WHERE p.paintingTitle = 'P2'";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(1, count[0]);
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeletePKIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeletePKIT.java
index 601802ee3..a771f8116 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeletePKIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLDeletePKIT.java
@@ -18,7 +18,7 @@
****************************************************************/
package org.apache.cayenne.access;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.unit.CayenneProjects;
@@ -64,9 +64,9 @@ public class DataContextEJBQLDeletePKIT {
EJBQLQuery query = new EJBQLQuery(ejbql);
query.setParameter("id", id);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(33, count[0]);
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
index b375a86b9..c13b33224 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLNumericalFunctionalIT.java
@@ -24,7 +24,7 @@ import java.math.BigInteger;
import java.util.List;
import org.apache.cayenne.Cayenne;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.numeric_types.BigDecimalEntity;
@@ -130,9 +130,9 @@ public class DataContextEJBQLNumericalFunctionalIT {
String ejbql = "UPDATE BooleanTestEntity AS p SET p.booleanColumn = true";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(3, count[0]);
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateCompoundIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateCompoundIT.java
index 75bf99ea5..7b322964f 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateCompoundIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateCompoundIT.java
@@ -20,7 +20,7 @@
package org.apache.cayenne.access;
import org.apache.cayenne.Cayenne;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.compound.CompoundPkTestEntity;
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -82,9 +83,9 @@ public class DataContextEJBQLUpdateCompoundIT {
EJBQLQuery query = new EJBQLQuery(ejbql);
query.setParameter("param", object);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateIT.java
index b8ec71639..27f3460cf 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextEJBQLUpdateIT.java
@@ -19,7 +19,7 @@
package org.apache.cayenne.access;
import org.apache.cayenne.Cayenne;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.testmap.Artist;
@@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import java.sql.Types;
+import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -80,9 +81,9 @@ public class DataContextEJBQLUpdateIT {
String ejbql = "UPDATE Painting AS p SET p.paintingTitle = 'XX' WHERE p.paintingTitle = 'P1'";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(1, count[0]);
@@ -104,9 +105,9 @@ public class DataContextEJBQLUpdateIT {
String ejbql = "UPDATE Painting AS p SET p.paintingTitle = 'XX'";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
@@ -128,9 +129,9 @@ public class DataContextEJBQLUpdateIT {
String ejbql = "UPDATE Painting AS p SET p.estimatedPrice = NULL";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
@@ -154,9 +155,9 @@ public class DataContextEJBQLUpdateIT {
// String ejbql = "UPDATE Painting AS p SET p.estimatedPrice = p.estimatedPrice * 2";
// EJBQLQuery query = new EJBQLQuery(ejbql);
//
- // QueryResponse result = context.performGenericQuery(query);
+ // List<QueryResultItem> result = context.performGenericQuery(query);
//
- // int[] count = result.firstUpdateCount();
+ // int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
// assertNotNull(count);
// assertEquals(1, count.length);
// assertEquals(2, count[0]);
@@ -178,9 +179,9 @@ public class DataContextEJBQLUpdateIT {
String ejbql = "UPDATE Painting AS p SET p.paintingTitle = 'XX', p.estimatedPrice = 1";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
@@ -202,9 +203,9 @@ public class DataContextEJBQLUpdateIT {
String ejbql = "UPDATE Painting AS p SET p.estimatedPrice = 1.1";
EJBQLQuery query = new EJBQLQuery(ejbql);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
@@ -230,9 +231,9 @@ public class DataContextEJBQLUpdateIT {
EJBQLQuery query = new EJBQLQuery(ejbql);
query.setParameter("artist", object);
- QueryResponse result = env.context().performGenericQuery(query);
+ List<QueryResultItem> result = env.context().performGenericQuery(query);
- int[] count = result.firstUpdateCount();
+ int[] count = ((QueryResultItem.Update) result.getFirst()).counts();
assertNotNull(count);
assertEquals(1, count.length);
assertEquals(2, count[0]);
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryIT.java
index 5d98c30d2..66fa01b42 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryIT.java
@@ -20,7 +20,7 @@
package org.apache.cayenne.access;
import org.apache.cayenne.DataRow;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.log.NoopSQLLogger;
import org.apache.cayenne.map.Procedure;
@@ -288,12 +288,12 @@ public class DataContextProcedureQueryIT {
ProcedureQuery q = new ProcedureQuery(OUT_STORED_PROCEDURE);
q.addParameter("in_param", 20);
- QueryResponse response = runProcedureGeneric(q);
+ List<QueryResultItem> response = runProcedureGeneric(q);
Map<String, ?> outParams = null;
- for (response.reset(); response.next(); ) {
- if (response.isOutParameters()) {
- outParams = response.currentOutParameters();
+ for (QueryResultItem item : response) {
+ if (item instanceof QueryResultItem.OutParameters out) {
+ outParams = out.values();
}
}
@@ -375,7 +375,7 @@ public class DataContextProcedureQueryIT {
assertTrue(id instanceof Long, "Expected Long, got: " + id.getClass().getName());
}
- protected QueryResponse runProcedureGeneric(ProcedureQuery q) throws Exception {
+ protected List<QueryResultItem> runProcedureGeneric(ProcedureQuery q) throws Exception {
BaseTransaction t = new ExternalTransaction(NoopSQLLogger.getInstance());
BaseTransaction.bindThreadTransaction(t);
diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataDomainFiltersIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataDomainFiltersIT.java
index abba0dbc1..36cd4895d 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/DataDomainFiltersIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/DataDomainFiltersIT.java
@@ -26,7 +26,7 @@ import org.apache.cayenne.DataChannelQueryFilterChain;
import org.apache.cayenne.DataChannelSyncFilter;
import org.apache.cayenne.DataChannelSyncFilterChain;
import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.annotation.PostPersist;
import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.query.ObjectSelect;
@@ -35,7 +35,6 @@ import org.apache.cayenne.testdo.testmap.Artist;
import org.apache.cayenne.unit.CayenneProjects;
import org.apache.cayenne.unit.CayenneTestsEnv;
import org.junit.jupiter.api.extension.RegisterExtension;
-import org.apache.cayenne.util.ListResponse;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -62,14 +61,14 @@ public class DataDomainFiltersIT {
DataChannelQueryFilter f1 = (originatingContext, query, iteratedResult, filterChain) -> {
results.add("f1start");
- QueryResponse response = filterChain.onQuery(originatingContext, query, iteratedResult);
+ List<QueryResultItem> response = filterChain.onQuery(originatingContext, query, iteratedResult);
results.add("f1end");
return response;
};
DataChannelQueryFilter f2 = (originatingContext, query, iteratedResult, filterChain) -> {
results.add("f2start");
- QueryResponse response = filterChain.onQuery(originatingContext, query, iteratedResult);
+ List<QueryResultItem> response = filterChain.onQuery(originatingContext, query, iteratedResult);
results.add("f2end");
return response;
};
@@ -78,7 +77,7 @@ public class DataDomainFiltersIT {
domain.queryFilters.add(f2);
ObjectSelect<Artist> query = ObjectSelect.query(Artist.class);
- QueryResponse response = domain.onQuery(env.context(), query, false);
+ List<QueryResultItem> response = domain.onQuery(env.context(), query, false);
assertNotNull(response);
assertEquals(4, results.size());
assertEquals("f2start", results.get(0));
@@ -128,8 +127,8 @@ public class DataDomainFiltersIT {
DataDomain domain = env.runtime().getDataDomain();
- QueryResponse r1 = new ListResponse();
- QueryResponse r2 = new ListResponse();
+ List<QueryResultItem> r1 = new ArrayList<>();
+ List<QueryResultItem> r2 = new ArrayList<>();
DataChannelQueryFilter f1 = (originatingContext, query, iteratedResult, filterChain) -> r1;
DataChannelQueryFilter f2 = (originatingContext, query, iteratedResult, filterChain) -> r2;
@@ -138,7 +137,7 @@ public class DataDomainFiltersIT {
domain.queryFilters.add(f2);
ObjectSelect<Artist> query = ObjectSelect.query(Artist.class);
- QueryResponse response = domain.onQuery(env.context(), query, false);
+ List<QueryResultItem> response = domain.onQuery(env.context(), query, false);
assertSame(r2, response);
}
@@ -167,8 +166,8 @@ public class DataDomainFiltersIT {
private List<String> results = new ArrayList<>();
@Override
- public QueryResponse onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
- DataChannelQueryFilterChain filterChain) {
+ public List<QueryResultItem> onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
+ DataChannelQueryFilterChain filterChain) {
results.add("onQuery");
return filterChain.onQuery(originatingContext, query, iteratedResult);
}
diff --git a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataDomainProviderIT.java b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataDomainProviderIT.java
index 8244e6303..d49a9cabe 100644
--- a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataDomainProviderIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DataDomainProviderIT.java
@@ -23,7 +23,7 @@ import org.apache.cayenne.DataChannelQueryFilterChain;
import org.apache.cayenne.DataChannelSyncFilter;
import org.apache.cayenne.DataChannelSyncFilterChain;
import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.access.DataDomain;
import org.apache.cayenne.access.DataNode;
import org.apache.cayenne.access.dbsync.SkipSchemaUpdateStrategy;
@@ -44,6 +44,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import javax.sql.DataSource;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -249,8 +250,8 @@ public class DataDomainProviderIT {
static class NoopQueryFilter implements DataChannelQueryFilter {
@Override
- public QueryResponse onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
- DataChannelQueryFilterChain filterChain) {
+ public List<QueryResultItem> onQuery(ObjectContext originatingContext, Query query, boolean iteratedResult,
+ DataChannelQueryFilterChain filterChain) {
return filterChain.onQuery(originatingContext, query, iteratedResult);
}
}
diff --git a/cayenne/src/test/java/org/apache/cayenne/query/MappedQueryIT.java b/cayenne/src/test/java/org/apache/cayenne/query/MappedQueryIT.java
index 475ad1981..a1215e638 100644
--- a/cayenne/src/test/java/org/apache/cayenne/query/MappedQueryIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/query/MappedQueryIT.java
@@ -19,7 +19,7 @@
package org.apache.cayenne.query;
import org.apache.cayenne.DataRow;
-import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.QueryResultItem;
import org.apache.cayenne.ResultBatchIterator;
import org.apache.cayenne.log.NoopSQLLogger;
import org.apache.cayenne.test.jdbc.TableHelper;
@@ -127,9 +127,10 @@ public class MappedQueryIT {
env.context().commitChanges();
- List<?> artists = runProcedureSelect(MappedSelect.query("ProcedureQuery", Artist.class)
+ List<QueryResultItem> result = runProcedureSelect(MappedSelect.query("ProcedureQuery", Artist.class)
.param("aName", "An Artist")
- .param("paintingPrice", 3000).forceNoCache()).firstList();
+ .param("paintingPrice", 3000).forceNoCache());
+ List<?> artists = ((QueryResultItem.Select<?>) result.getFirst()).objects();
// check the results
assertNotNull(artists, "Null result from StoredProcedure.");
@@ -165,7 +166,7 @@ public class MappedQueryIT {
query3.getMetaData(env.context().getEntityResolver()).getCacheKey());
}
- protected QueryResponse runProcedureSelect(AbstractMappedQuery q) throws Exception {
+ protected List<QueryResultItem> runProcedureSelect(AbstractMappedQuery q) throws Exception {
// Sybase blows whenever a transaction wraps a SP, so turn off
// transactions
diff --git a/cayenne/src/test/java/org/apache/cayenne/util/GenericResponseTest.java b/cayenne/src/test/java/org/apache/cayenne/util/GenericResponseTest.java
deleted file mode 100644
index 8a02c37bb..000000000
--- a/cayenne/src/test/java/org/apache/cayenne/util/GenericResponseTest.java
+++ /dev/null
@@ -1,72 +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.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class GenericResponseTest {
-
- @Test
- public void creation() {
- List list = new ArrayList();
- list.add(new HashMap());
-
- GenericResponse r = new GenericResponse();
- r.addBatchUpdateCount(new int[] { 1, 2, 3 });
- r.addResultList(list);
-
- assertEquals(2, r.size());
-
- assertTrue(r.next());
- assertFalse(r.isList());
-
- int[] srInt = r.currentUpdateCount();
- assertEquals(3, srInt.length);
- assertEquals(2, srInt[1]);
-
- assertTrue(r.next());
- assertTrue(r.isList());
-
- assertEquals(list, r.currentList());
-
- assertFalse(r.next());
- }
-
- @Test
- public void next() {
- 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());
- }
-
-}
diff --git a/cayenne/src/test/java/org/apache/cayenne/util/ListResponseTest.java b/cayenne/src/test/java/org/apache/cayenne/util/ListResponseTest.java
deleted file mode 100644
index 6fb0c9927..000000000
--- a/cayenne/src/test/java/org/apache/cayenne/util/ListResponseTest.java
+++ /dev/null
@@ -1,67 +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.junit.jupiter.api.Test;
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class ListResponseTest {
-
- @Test
- public void creation() {
-
- Object object = new Object();
- ListResponse r = new ListResponse(object);
-
- assertEquals(1, r.size());
-
- assertTrue(r.next());
- assertTrue(r.isList());
-
- List currentList = r.currentList();
- assertEquals(1, currentList.size());
- assertTrue(currentList.contains(object));
-
- assertFalse(r.next());
-
- r.reset();
- assertTrue(r.next());
-
- assertSame(currentList, r.firstList());
- }
-
- @Test
- public void next() {
- 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());
- }
-
-}
diff --git a/docs/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc b/docs/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc
index 8aa9eeb49..02ccb2b41 100644
--- a/docs/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc
+++ b/docs/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/objectContext.adoc
@@ -84,7 +84,15 @@ While not commonly used, it is nevertheless important in some situations. E.g.:
[source, java]
----
-QueryResponse response = context.performGenericQuery(query);
+List<QueryResultItem> result = context.performGenericQuery(query);
+for (QueryResultItem item : result) {
+ switch (item) {
+ case QueryResultItem.Select<?> select -> process(select.objects());
+ case QueryResultItem.Update update -> process(update.counts());
+ case QueryResultItem.Iterator<?> iterator -> process(iterator.iterator());
+ case QueryResultItem.OutParameters out -> process(out.values());
+ }
+}
----
When multiple such queries need to run together, execute them inside an explicit transaction: