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
The following commit(s) were added to refs/heads/master by this push:
new ef17c9402 CAY-2988 Nested iterated queries transaction handling
ef17c9402 is described below
commit ef17c9402a3e57d2fd837f752cf1c473dd11e240
Author: Andrus Adamchik <[email protected]>
AuthorDate: Mon Aug 17 12:44:55 2026 -0400
CAY-2988 Nested iterated queries transaction handling
---
RELEASE-NOTES.txt | 1 +
.../cayenne/access/DataDomainQueryAction.java | 40 ++++++++++++----------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index f65d75126..e3e641c2b 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -51,6 +51,7 @@ CAY-2968 Vertical Inheritance: INSERT instead of UPDATE after
updating flattened
CAY-2973 Exception trying to copy/paste a callback
CAY-2976 Exception creating a relationship for an Incomplete ObjEntity
CAY-2977 DbImport confused by multi-key relationships
+CAY-2988 Nested iterated queries transaction handling
----------------------------------
Release: 5.0-M2
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 e6e80d8f2..bfe9fb3a6 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
@@ -73,6 +73,7 @@ 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;
@@ -179,16 +180,27 @@ class DataDomainQueryAction implements QueryRouter,
OperationObserver {
}
private void performIteratedQuery() {
- Transaction tx = BaseTransaction.getThreadTransaction();
- if (tx != null) {
- runIteratedQuery(tx);
- } else {
- tx = domain.getTransactionFactory().createTransaction();
+
+ // 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");
+ }
+
+ // The transaction will be owned by the iterator, and will outlive the
query action
+ else {
+ Transaction tx =
domain.getTransactionFactory().createTransaction();
BaseTransaction.bindThreadTransaction(tx);
try {
- runIteratedQuery(tx);
- } catch (Exception e) {
- throw new CayenneRuntimeException(e);
+ runQuery();
+ ResultIterator<?> it =
Objects.requireNonNull(fullResponse.firstIterator(), "Iterator response
expected");
+ fullResponse.replaceResult(it, 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
+ // transaction on its own, but a failure may as well originate
outside of the node.
+ tx.setRollbackOnly();
+ throw th;
} finally {
BaseTransaction.bindThreadTransaction(null);
if (tx.isRollbackOnly()) {
@@ -199,6 +211,8 @@ class DataDomainQueryAction implements QueryRouter,
OperationObserver {
}
}
}
+
+ fullResponse.reset();
}
private boolean interceptOIDQuery() {
@@ -525,16 +539,6 @@ class DataDomainQueryAction implements QueryRouter,
OperationObserver {
}
}
- private void runIteratedQuery(Transaction tx) {
- runQuery();
- ResultIterator<?> iterator = fullResponse.firstIterator();
- if (iterator == null) {
- throw new IllegalStateException("Iterator response expected");
- }
- fullResponse.replaceResult(iterator, new
TransactionResultIteratorDecorator<>(iterator, tx));
- fullResponse.reset();
- }
-
@SuppressWarnings({"unchecked", "rawtypes"})
private void interceptObjectConversion() {
if (noObjectConversion()) {