You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/12/24 15:49:01 UTC

[isis] 01/02: ISIS-2033: dn-integration: JdoPersistenceSession to no longer access TransactionService directly

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

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

commit 409d2a92cffcbfc48b8d7b823771e54bbeb8bfaa
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Dec 24 15:29:39 2020 +0100

    ISIS-2033: dn-integration: JdoPersistenceSession to no longer access
    TransactionService directly
---
 .../applib/services/xactn/TransactionService.java  |   6 +-
 .../isis/commons/internal/collections/_Inbox.java  |  12 +-
 .../integration/jdosupport/IsisJdoSupportDN5.java  |  10 +-
 .../lifecycles/IsisLifecycleListener.java          |   7 +-
 .../fetching/EntityFetchResultHandler.java}        |  11 +-
 .../fetching/ValueFetchResultHandler.java}         |   9 +-
 .../persistence/JdoPersistenceSession.java         |   6 -
 .../persistence/JdoPersistenceSession5.java        |  56 ++++----
 .../persistence/_JdoPersistenceSessionBase.java    |  10 +-
 .../persistence/command/CreateObjectCommand.java   |   9 +-
 .../persistence/command/DeleteObjectCommand.java   |  11 +-
 ...Processor.java => PersistenceQueryContext.java} |  16 ++-
 .../PersistenceQueryFindAllInstancesProcessor.java |  12 +-
 ...sistenceQueryFindUsingApplibQueryProcessor.java |  32 +++--
 .../queries/PersistenceQueryProcessor.java         |   2 +-
 .../queries/PersistenceQueryProcessorAbstract.java |  14 +-
 .../persistence/query/PersistenceQuery.java        |   4 +-
 .../persistence/query/PersistenceQueryFactory.java |  22 ++--
 .../query/PersistenceQueryFindAllInstances.java    |   6 +-
 ...ersistenceQueryFindUsingApplibQueryDefault.java |   6 +-
 .../TransactionalCommandProcessor.java}            |  15 ++-
 .../transaction/TxManagerInternalFactory.java      |   3 +-
 .../jdo/integration/transaction/_CommandQueue.java | 145 +++++++++++++++++++++
 .../jdo/integration/transaction/_Tx.java           | 135 +++----------------
 .../jdo/integration/transaction/_TxHelper.java     |   6 +-
 .../transaction/_TxManagerInternal.java            |  33 +++--
 26 files changed, 327 insertions(+), 271 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
index bc232bb..eccf794 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
@@ -19,7 +19,6 @@
 
 package org.apache.isis.applib.services.xactn;
 
-import java.util.Optional;
 import java.util.concurrent.Callable;
 
 import org.apache.isis.commons.functional.Result;
@@ -78,10 +77,9 @@ public interface TransactionService {
      *
      * @param runnable
      */
-    default Optional<Throwable> executeWithinTransaction(ThrowingRunnable runnable) {
+    default Result<Void> executeWithinTransaction(ThrowingRunnable runnable) {
         val callable = ThrowingRunnable.toCallable(runnable);
-        return executeWithinTransaction(callable)
-                .failure();
+        return executeWithinTransaction(callable);
     }
 
 
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Inbox.java b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Inbox.java
index 243475a..cdbc8dd 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Inbox.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Inbox.java
@@ -22,6 +22,8 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.isis.commons.collections.Can;
+
 import lombok.val;
 
 /**
@@ -48,16 +50,16 @@ public class _Inbox<T> implements Serializable {
         }
     }
 
-    public List<T> snapshot() {
+    public Can<T> snapshot() {
         synchronized($lock) {
-            val defensiveCopy = _Lists.newArrayList(list);
-            return defensiveCopy;
+            // defensiveCopy
+            return Can.ofCollection(list);
         }
     }
 
-    public List<T> snapshotThenClear() {
+    public Can<T> snapshotThenClear() {
         synchronized($lock) {
-            val defensiveCopy = _Lists.newArrayList(list);
+            val defensiveCopy = Can.ofCollection(list);
             list.clear();
             return defensiveCopy;
         }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/jdosupport/IsisJdoSupportDN5.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/jdosupport/IsisJdoSupportDN5.java
index 492d96d..8d6af46 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/jdosupport/IsisJdoSupportDN5.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/jdosupport/IsisJdoSupportDN5.java
@@ -19,6 +19,8 @@
 
 package org.apache.isis.persistence.jdo.integration.jdosupport;
 
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
@@ -47,11 +49,11 @@ import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.core.interaction.session.InteractionTracker;
 import org.apache.isis.core.metamodel.adapter.oid.ObjectPersistenceException;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.persistence.jdo.applib.services.IsisJdoSupport_v3_2;
 import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession;
 
-import static org.apache.isis.commons.internal.base._NullSafe.stream;
+import lombok.val;
 
 
 /**
@@ -66,15 +68,15 @@ import static org.apache.isis.commons.internal.base._NullSafe.stream;
 public class IsisJdoSupportDN5 implements IsisJdoSupport_v3_2 {
 
     @Inject private InteractionTracker isisInteractionTracker;
+    @Inject private MetaModelContext mmc;
     
     @Override
     public <T> T refresh(final T domainObject) {
-        final ManagedObject adapter = getPersistenceSession().adapterFor(domainObject);
+        val adapter = mmc.getObjectManager().adapt(domainObject); 
         getPersistenceSession().refreshEntity(adapter);
         return domainObject;
     }
 
-
     @Override
     public void ensureLoaded(final Collection<?> domainObjects) {
         getPersistenceSession().getPersistenceManager().retrieveAll(domainObjects);
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/IsisLifecycleListener.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/IsisLifecycleListener.java
index 757f67c..0691e83 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/IsisLifecycleListener.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/IsisLifecycleListener.java
@@ -33,7 +33,7 @@ import javax.jdo.listener.StoreLifecycleListener;
 import org.datanucleus.enhancement.Persistable;
 
 import org.apache.isis.commons.internal.collections._Maps;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.persistence.jdo.integration.lifecycles.fetching.EntityFetchResultHandler;
 
 public class IsisLifecycleListener
 implements AttachLifecycleListener, ClearLifecycleListener, CreateLifecycleListener, DeleteLifecycleListener,
@@ -42,9 +42,8 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif
     /**
      * The internal contract between PersistenceSession and this class.
      */
-    public interface PersistenceSessionLifecycleManagement {
-
-        ManagedObject initializeEntity(Persistable pojo);
+    public interface PersistenceSessionLifecycleManagement 
+    extends EntityFetchResultHandler {
 
         void invokeIsisPersistingCallback(Persistable pojo);
         void enlistCreatedAndInvokeIsisPersistedCallback(Persistable pojo);
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/PersistenceCommandQueue.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/fetching/EntityFetchResultHandler.java
similarity index 75%
copy from persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/PersistenceCommandQueue.java
copy to persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/fetching/EntityFetchResultHandler.java
index 66ad7bb..9c18b34 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/PersistenceCommandQueue.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/fetching/EntityFetchResultHandler.java
@@ -16,13 +16,14 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.integration.persistence.command;
+package org.apache.isis.persistence.jdo.integration.lifecycles.fetching;
 
-public interface PersistenceCommandQueue {
+import org.datanucleus.enhancement.Persistable;
 
-    void addCommand(PersistenceCommand persistenceCommand);
+import org.apache.isis.core.metamodel.spec.ManagedObject;
 
-    //void createObject(ManagedObject adapter);
-    //void deleteObject(ManagedObject adapter);
+public interface EntityFetchResultHandler {
     
+    ManagedObject initializeEntity(Persistable pojo);
+
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/PersistenceCommandQueue.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/fetching/ValueFetchResultHandler.java
similarity index 75%
rename from persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/PersistenceCommandQueue.java
rename to persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/fetching/ValueFetchResultHandler.java
index 66ad7bb..2cf457c 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/PersistenceCommandQueue.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/lifecycles/fetching/ValueFetchResultHandler.java
@@ -16,13 +16,12 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.integration.persistence.command;
+package org.apache.isis.persistence.jdo.integration.lifecycles.fetching;
 
-public interface PersistenceCommandQueue {
+import org.apache.isis.core.metamodel.spec.ManagedObject;
 
-    void addCommand(PersistenceCommand persistenceCommand);
+public interface ValueFetchResultHandler {
 
-    //void createObject(ManagedObject adapter);
-    //void deleteObject(ManagedObject adapter);
+    ManagedObject adapterFor(Object pojo);
     
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession.java
index b1b42a9..233f82a 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession.java
@@ -21,8 +21,6 @@ package org.apache.isis.persistence.jdo.integration.persistence;
 import java.rmi.NoSuchObjectException;
 import java.util.Optional;
 
-import javax.annotation.Nullable;
-
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.context.HasMetaModelContext;
@@ -41,10 +39,6 @@ extends
 
     void open();
     void close();
-
-    default ManagedObject adapterFor(@Nullable Object pojo) {
-        return _Utils.adapterFor(getMetaModelContext(), pojo);
-    }
     
     /**
      * Forces a reload (refresh in JDO terminology) of the domain object
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java
index c645926..d2dd1db 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/JdoPersistenceSession5.java
@@ -20,6 +20,7 @@ package org.apache.isis.persistence.jdo.integration.persistence;
 
 import java.util.Optional;
 
+import javax.annotation.Nullable;
 import javax.enterprise.inject.Vetoed;
 import javax.jdo.FetchGroup;
 import javax.jdo.PersistenceManager;
@@ -29,7 +30,6 @@ import org.datanucleus.enhancement.Persistable;
 
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
-import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.adapter.oid.ObjectNotFoundException;
 import org.apache.isis.core.metamodel.adapter.oid.Oid;
@@ -49,6 +49,7 @@ import org.apache.isis.persistence.jdo.integration.lifecycles.JdoStoreLifecycleL
 import org.apache.isis.persistence.jdo.integration.lifecycles.LoadLifecycleListenerForIsis;
 import org.apache.isis.persistence.jdo.integration.persistence.command.CreateObjectCommand;
 import org.apache.isis.persistence.jdo.integration.persistence.command.DeleteObjectCommand;
+import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryContext;
 import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQuery;
 
 import lombok.NonNull;
@@ -60,9 +61,10 @@ import lombok.extern.log4j.Log4j2;
  */
 @Vetoed @Log4j2
 public class JdoPersistenceSession5 extends _JdoPersistenceSessionBase
-implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
+implements
+    PersistenceQueryContext,
+    IsisLifecycleListener.PersistenceSessionLifecycleManagement {
 
-    private final TransactionService transactionService;
     private Runnable unregisterLifecycleListeners;
 
     /**
@@ -76,7 +78,6 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
             final FixturesInstalledStateHolder stateHolder) {
 
         super(metaModelContext, jdoPersistenceManagerFactory, stateHolder);
-        this.transactionService = metaModelContext.getTransactionService();
     }
 
     // -- open
@@ -154,6 +155,11 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
     }
 
     @Override
+    public ManagedObject adapterFor(final @Nullable Object pojo) {
+        return _Utils.adapterFor(getMetaModelContext(), pojo);
+    }
+    
+    @Override
     public Can<ManagedObject> allMatchingQuery(final Query<?> query) {
         val instances = findInstancesInTransaction(query, QueryCardinality.MULTIPLE);
         return instances;
@@ -188,12 +194,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
             log.debug("maps to (core runtime) PersistenceQuery: {}", persistenceQuery);
         }
 
-        final Can<ManagedObject> instances = transactionService
-                .executeWithinTransaction(
-                        ()->persistenceQuery.execute(this) )
-                .orElseFail();
-        
-        return instances;
+        return txCommandProcessor.executeWithinTransaction(this, persistenceQuery);
     }
 
     /**
@@ -205,7 +206,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
             final QueryCardinality cardinality) {
 
         final PersistenceQuery persistenceQuery =
-                persistenceQueryFactory.createPersistenceQueryFor(query, cardinality);
+                persistenceQueryFactory.createPersistenceQueryFor(this::adapterFor, query, cardinality);
         if (persistenceQuery == null) {
             throw new IllegalArgumentException("Unknown Query type: " + query.getDescription());
         }
@@ -294,45 +295,42 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
 
 
     @Override
-    public void makePersistentInTransaction(final ManagedObject adapter) {
+    public void makePersistentInTransaction(final ManagedObject entity) {
         
-        val pojo = adapter.getPojo();
+        log.debug("about to persist entity {}", entity);
+        state.ensureOpened();
+        
+        val pojo = entity.getPojo();
         
         if (DnEntityStateProvider.entityState(pojo).isAttached()) {
-            throw new NotPersistableException("Object already persistent: " + adapter);
+            throw new NotPersistableException("Object already persistent: " + entity);
         }
-        val spec = adapter.getSpecification();
+        val spec = entity.getSpecification();
         if (spec.isManagedBean()) {
-            throw new NotPersistableException("Can only persist entity beans: "+ adapter);
+            throw new NotPersistableException("Can only persist entity beans: "+ entity);
         }
         if (spec.getBeanSort().isCollection()) {
             //XXX not sure if we can do better than that, eg. traverse each element of the collection and persist individually
-            throw new NotPersistableException("Cannot persist a collection: " + adapter);
+            throw new NotPersistableException("Cannot persist a collection: " + entity);
         }
         
-        log.debug("persist {}", adapter);
-        state.ensureOpened();
-        
-        transactionService.executeWithinTransaction(()->{
-            commandQueue.addCommand(newCreateObjectCommand(adapter));
-        });
+        txCommandProcessor.executeWithinTransaction(newCreateObjectCommand(entity));
     }
     
     // -- destroyObjectInTransaction
 
     @Override
-    public void destroyObjectInTransaction(final ManagedObject adapter) {
-        val spec = adapter.getSpecification();
+    public void destroyObjectInTransaction(final ManagedObject entity) {
+        
+        val spec = entity.getSpecification();
         if (spec.isParented()) {
             return;
         }
         
-        log.debug("deleteObject {}", adapter);
+        log.debug("about to delete entity {}", entity);
         state.ensureOpened();
         
-        transactionService.executeWithinTransaction(()->{
-            commandQueue.addCommand(newDeleteObjectCommand(adapter));
-        });
+        txCommandProcessor.executeWithinTransaction(newDeleteObjectCommand(entity));
     }
 
     // -- newXxxCommand
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/_JdoPersistenceSessionBase.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/_JdoPersistenceSessionBase.java
index 1b94254..f27de76 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/_JdoPersistenceSessionBase.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/_JdoPersistenceSessionBase.java
@@ -28,8 +28,8 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.transaction.changetracking.EntityChangeTracker;
 import org.apache.isis.persistence.jdo.applib.fixturestate.FixturesInstalledStateHolder;
-import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommandQueue;
 import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQueryFactory;
+import org.apache.isis.persistence.jdo.integration.transaction.TransactionalCommandProcessor;
 import org.apache.isis.persistence.jdo.integration.transaction.TxManagerInternalFactory;
 
 import lombok.Getter;
@@ -52,7 +52,7 @@ implements JdoPersistenceSession {
      */
     protected final PersistenceManagerFactory jdoPersistenceManagerFactory;
     
-    PersistenceCommandQueue commandQueue;
+    TransactionalCommandProcessor txCommandProcessor;
 
     /**
      * populated only when {@link #open()}ed.
@@ -79,10 +79,8 @@ implements JdoPersistenceSession {
         this.fixturesInstalledStateHolder = fixturesInstalledStateHolder;
 
         // sub-components
-        this.persistenceQueryFactory = PersistenceQueryFactory.of(
-                obj->this.adapterFor(obj), 
-                metaModelContext.getSpecificationLoader());
-        this.commandQueue = TxManagerInternalFactory.newCommandQueue(metaModelContext, this); 
+        this.persistenceQueryFactory = PersistenceQueryFactory.of(metaModelContext);
+        this.txCommandProcessor = TxManagerInternalFactory.newCommandQueue(metaModelContext, this); 
 
         this.state = State.NOT_INITIALIZED;
     }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/CreateObjectCommand.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/CreateObjectCommand.java
index 1ac5acd..5b7b37c 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/CreateObjectCommand.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/CreateObjectCommand.java
@@ -25,14 +25,14 @@ import org.apache.isis.persistence.jdo.datanucleus.entities.DnEntityStateProvide
 
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
+import lombok.ToString;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
-@RequiredArgsConstructor
+@RequiredArgsConstructor @ToString
 @Log4j2
 public class CreateObjectCommand implements PersistenceCommand {
 
-    //private final PersistenceManager persistenceManager;
     @Getter private final ManagedObject entity;
 
     @Override
@@ -52,9 +52,4 @@ public class CreateObjectCommand implements PersistenceCommand {
         persistenceManager.makePersistent(domainObject);
     }
 
-    @Override
-    public String toString() {
-        return "CreateObjectCommand [entity=" + entity + "]";
-    }
-
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/DeleteObjectCommand.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/DeleteObjectCommand.java
index 612033a..a3351ae 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/DeleteObjectCommand.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/command/DeleteObjectCommand.java
@@ -24,26 +24,21 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
+import lombok.ToString;
 import lombok.extern.log4j.Log4j2;
 
-@RequiredArgsConstructor
+@RequiredArgsConstructor @ToString
 @Log4j2
 public class DeleteObjectCommand implements PersistenceCommand {
 
-    //private final PersistenceManager persistenceManager;
     @Getter private final ManagedObject entity;
     
     @Override
     public void execute(PersistenceManager persistenceManager) {
         if (log.isDebugEnabled()) {
-            log.debug("destroy object - executing command for {}", entity);
+            log.debug("delete object - executing command for {}", entity);
         }
         persistenceManager.deletePersistent(entity.getPojo());
     }
 
-    @Override
-    public String toString() {
-        return "DeleteObjectCommand [entity=" + entity + "]";
-    }
-
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryContext.java
similarity index 68%
copy from persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
copy to persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryContext.java
index fa0e991..ad48b30 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryContext.java
@@ -18,12 +18,14 @@
  */
 package org.apache.isis.persistence.jdo.integration.persistence.queries;
 
-import org.apache.isis.commons.collections.Can;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQuery;
+import org.apache.isis.persistence.jdo.integration.lifecycles.fetching.EntityFetchResultHandler;
+import org.apache.isis.persistence.jdo.integration.lifecycles.fetching.ValueFetchResultHandler;
+import org.apache.isis.persistence.jdo.provider.persistence.HasPersistenceManager;
 
-public interface PersistenceQueryProcessor<T extends PersistenceQuery> {
-    
-    Can<ManagedObject> process(T query);
-}
+public interface PersistenceQueryContext 
+extends
+    ValueFetchResultHandler,
+    EntityFetchResultHandler,
+    HasPersistenceManager {
 
+}
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindAllInstancesProcessor.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindAllInstancesProcessor.java
index 9b1ded8..6e65852 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindAllInstancesProcessor.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindAllInstancesProcessor.java
@@ -20,7 +20,6 @@ package org.apache.isis.persistence.jdo.integration.persistence.queries;
 
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession5;
 import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQueryFindAllInstances;
 
 import lombok.val;
@@ -30,12 +29,10 @@ import lombok.extern.log4j.Log4j2;
 public class PersistenceQueryFindAllInstancesProcessor 
 extends PersistenceQueryProcessorAbstract<PersistenceQueryFindAllInstances> {
 
-    public PersistenceQueryFindAllInstancesProcessor(final JdoPersistenceSession5 persistenceSession) {
-        super(persistenceSession);
-    }
-
     @Override
-    public Can<ManagedObject> process(final PersistenceQueryFindAllInstances persistenceQuery) {
+    public Can<ManagedObject> process(
+            final PersistenceQueryContext queryContext,
+            final PersistenceQueryFindAllInstances persistenceQuery) {
 
         val spec = persistenceQuery.getSpecification();
         val cls = spec.getCorrespondingClass();
@@ -51,8 +48,9 @@ extends PersistenceQueryProcessorAbstract<PersistenceQueryFindAllInstances> {
         }
 
         val pojos = isisJdoSupport.executeQuery(cls);
-        return loadAdapters(pojos);
+        return loadAdapters(queryContext, pojos);
 
     }
 
+
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindUsingApplibQueryProcessor.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindUsingApplibQueryProcessor.java
index 4b5c194..5bce084 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindUsingApplibQueryProcessor.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryFindUsingApplibQueryProcessor.java
@@ -33,38 +33,40 @@ import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.persistence.jdo.integration.metamodel.JdoPropertyUtils;
-import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession5;
 import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQueryFindUsingApplibQueryDefault;
 
+import lombok.RequiredArgsConstructor;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
+@RequiredArgsConstructor
 @Log4j2
-public class PersistenceQueryFindUsingApplibQueryProcessor extends PersistenceQueryProcessorAbstract<PersistenceQueryFindUsingApplibQueryDefault> {
-
-    public PersistenceQueryFindUsingApplibQueryProcessor(final JdoPersistenceSession5 persistenceSession) {
-        super(persistenceSession);
-    }
+public class PersistenceQueryFindUsingApplibQueryProcessor 
+extends PersistenceQueryProcessorAbstract<PersistenceQueryFindUsingApplibQueryDefault> {
 
     @Override
-    public Can<ManagedObject> process(final PersistenceQueryFindUsingApplibQueryDefault persistenceQuery) {
+    public Can<ManagedObject> process(
+            final PersistenceQueryContext queryContext,
+            final PersistenceQueryFindUsingApplibQueryDefault persistenceQuery) {
         final String queryName = persistenceQuery.getQueryName();
         final ObjectSpecification objectSpec = persistenceQuery.getSpecification();
 
         final List<?> results;
         if((objectSpec.getFullIdentifier() + "#pk").equals(queryName)) {
-            results = getResultsPk(persistenceQuery);
+            results = getResultsPk(queryContext, persistenceQuery);
         } else {
-            results = getResults(persistenceQuery);
+            results = getResults(queryContext, persistenceQuery);
         }
 
-        return loadAdapters(results);
+        return loadAdapters(queryContext, results);
     }
 
     // -- HELPER
 
     // special case handling
-    private List<?> getResultsPk(final PersistenceQueryFindUsingApplibQueryDefault persistenceQuery) {
+    private List<?> getResultsPk(
+            final PersistenceQueryContext queryContext,
+            final PersistenceQueryFindUsingApplibQueryDefault persistenceQuery) {
 
         val queryName = persistenceQuery.getQueryName();
         final Map<String, Object> map = unwrap(persistenceQuery.getArgumentsAdaptersByParameterName());
@@ -84,7 +86,7 @@ public class PersistenceQueryFindUsingApplibQueryProcessor extends PersistenceQu
         /* XXX[ISIS-2020] as of Oct. 2018: likely not working on FederatedDataStore
          * see PersistenceQueryFindAllInstancesProcessor for workaround using type-safe query instead
          */
-        final Query<?> jdoQuery = persistenceSession.newJdoQuery(cls, filter);
+        final Query<?> jdoQuery = queryContext.newJdoQuery(cls, filter);
         isisJdoSupport.disableMultivaluedFetch(jdoQuery); // fetch optimization
 
         if (log.isDebugEnabled()) {
@@ -99,7 +101,9 @@ public class PersistenceQueryFindUsingApplibQueryProcessor extends PersistenceQu
         }
     }
 
-    private List<?> getResults(final PersistenceQueryFindUsingApplibQueryDefault persistenceQuery) {
+    private List<?> getResults(
+            final PersistenceQueryContext queryContext,
+            final PersistenceQueryFindUsingApplibQueryDefault persistenceQuery) {
 
         val queryName = persistenceQuery.getQueryName();
         final Map<String, Object> argumentsByParameterName = unwrap(
@@ -114,7 +118,7 @@ public class PersistenceQueryFindUsingApplibQueryProcessor extends PersistenceQu
         /* XXX[ISIS-2020] as of Oct. 2018: likely not working on FederatedDataStore
          * see PersistenceQueryFindAllInstancesProcessor for workaround using type-safe query instead 
          */
-        final Query<?> jdoQuery = persistenceSession.newJdoNamedQuery(cls, queryName); 
+        final Query<?> jdoQuery = queryContext.newJdoNamedQuery(cls, queryName); 
         isisJdoSupport.disableMultivaluedFetch(jdoQuery);
 
         if(persistenceQuery.hasRange()) {
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
index fa0e991..5d61123 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
@@ -24,6 +24,6 @@ import org.apache.isis.persistence.jdo.integration.persistence.query.Persistence
 
 public interface PersistenceQueryProcessor<T extends PersistenceQuery> {
     
-    Can<ManagedObject> process(T query);
+    Can<ManagedObject> process(PersistenceQueryContext queryContext, T query);
 }
 
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessorAbstract.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessorAbstract.java
index 49879cd..c12b1ac 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessorAbstract.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessorAbstract.java
@@ -31,7 +31,6 @@ import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.persistence.jdo.applib.services.IsisJdoSupport_v3_2;
 import org.apache.isis.persistence.jdo.integration.lifecycles.IsisLifecycleListener;
-import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession5;
 import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQuery;
 
 import lombok.val;
@@ -40,19 +39,12 @@ public abstract class PersistenceQueryProcessorAbstract<T extends PersistenceQue
 implements PersistenceQueryProcessor<T> {
 
 
-    final JdoPersistenceSession5 persistenceSession;
-
-    protected PersistenceQueryProcessorAbstract(final JdoPersistenceSession5 persistenceSession) {
-        this.persistenceSession = persistenceSession;
-    }
-
-
     /**
      * Traversing the provided list causes (or should cause) the
      * {@link IsisLifecycleListener#postLoad(InstanceLifecycleEvent) {
      * to be called.
      */
-    protected Can<ManagedObject> loadAdapters(final List<?> pojos) {
+    protected Can<ManagedObject> loadAdapters(final PersistenceQueryContext queryContext, final List<?> pojos) {
         val adapters = _Lists.<ManagedObject>newArrayList();
         for (val pojo : pojos) {
             // ought not to be necessary, however for some queries it seems that the
@@ -60,11 +52,11 @@ implements PersistenceQueryProcessor<T> {
             ManagedObject adapter;
             if(pojo instanceof Persistable) {
                 // an entity
-                adapter = persistenceSession.initializeEntity((Persistable) pojo);
+                adapter = queryContext.initializeEntity((Persistable) pojo);
                 _Assert.assertNotNull(adapter);
             } else {
                 // a value type
-                adapter = persistenceSession.adapterFor(pojo);
+                adapter = queryContext.adapterFor(pojo);
                 _Assert.assertNotNull(adapter);
             }
             adapters.add(adapter);
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQuery.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQuery.java
index 74ec7cd..b8b300e 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQuery.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQuery.java
@@ -23,7 +23,7 @@ import org.apache.isis.applib.query.Query;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession5;
+import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryContext;
 
 /**
  * Defines a criteria for including instances in set, corresponds to
@@ -47,5 +47,5 @@ public interface PersistenceQuery {
      */
     public long getCount() ;
 
-    Can<ManagedObject> execute(JdoPersistenceSession5 persistenceSession);
+    Can<ManagedObject> execute(PersistenceQueryContext queryContext);
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFactory.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFactory.java
index cf43d99..e6fc8df 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFactory.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFactory.java
@@ -26,25 +26,28 @@ import org.apache.isis.applib.query.NamedQuery;
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.context.HasMetaModelContext;
+import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.metamodel.services.container.query.QueryCardinality;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 
+import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.log4j.Log4j2;
 
 @RequiredArgsConstructor(staticName = "of") @Log4j2
-public class PersistenceQueryFactory {
+public class PersistenceQueryFactory implements HasMetaModelContext {
 
-    private final Function<Object, ManagedObject> adapterProvider;
-    private final SpecificationLoader specificationLoader;
+    @Getter(onMethod_ = {@Override})
+    private final MetaModelContext metaModelContext;
 
     /**
      * Converts the {@link org.apache.isis.applib.query.Query applib representation of a query} into the
      * {@link PersistenceQuery NOF-internal representation}.
      */
     public final PersistenceQuery createPersistenceQueryFor(
+            final Function<Object, ManagedObject> adapterProvider,
             final Query<?> query, 
             final QueryCardinality cardinality) {
         
@@ -60,9 +63,9 @@ public class PersistenceQueryFactory {
             final NamedQuery<?> queryDefault = (NamedQuery<?>) query;
             final String queryName = queryDefault.getName();
             final Map<String, ManagedObject> parametersByName = 
-                    wrap(queryDefault.getParametersByName());
+                    wrap(adapterProvider, queryDefault.getParametersByName());
             return new PersistenceQueryFindUsingApplibQueryDefault(noSpec, queryName, parametersByName, cardinality,
-                    specificationLoader, queryDefault.getStart(), queryDefault.getCount());
+                    getSpecificationLoader(), queryDefault.getStart(), queryDefault.getCount());
         }
         throw _Exceptions.unsupportedOperation("query type %s not supported by this persistence implementation",
                 query.getClass());
@@ -72,7 +75,10 @@ public class PersistenceQueryFactory {
      * Converts a map of pojos keyed by string to a map of adapters keyed by the
      * same strings.
      */
-    private Map<String, ManagedObject> wrap(final Map<String, Object> argumentsByParameterName) {
+    private Map<String, ManagedObject> wrap(
+            final Function<Object, ManagedObject> adapterProvider,
+            final Map<String, Object> argumentsByParameterName) {
+        
         final Map<String, ManagedObject> argumentsAdaptersByParameterName = _Maps.newHashMap();
         for (final Map.Entry<String, Object> entry : argumentsByParameterName.entrySet()) {
             final String parameterName = entry.getKey();
@@ -84,7 +90,7 @@ public class PersistenceQueryFactory {
     }
 
     private ObjectSpecification specFor(final Query<?> query) {
-        return specificationLoader.loadSpecification(query.getResultType());
+        return getSpecificationLoader().loadSpecification(query.getResultType());
     }
 
 
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindAllInstances.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindAllInstances.java
index 1334b2b..baddb24 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindAllInstances.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindAllInstances.java
@@ -24,7 +24,7 @@ import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.commons.ToString;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession5;
+import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryContext;
 import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryFindAllInstancesProcessor;
 
 /**
@@ -66,7 +66,7 @@ public class PersistenceQueryFindAllInstances extends PersistenceQueryAbstract
     }
 
     @Override
-    public Can<ManagedObject> execute(JdoPersistenceSession5 persistenceSession) {
-        return new PersistenceQueryFindAllInstancesProcessor(persistenceSession).process(this);
+    public Can<ManagedObject> execute(PersistenceQueryContext queryContext) {
+        return new PersistenceQueryFindAllInstancesProcessor().process(queryContext, this);
     }
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindUsingApplibQueryDefault.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindUsingApplibQueryDefault.java
index 691b5d9..4ba4162 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindUsingApplibQueryDefault.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/query/PersistenceQueryFindUsingApplibQueryDefault.java
@@ -29,7 +29,7 @@ import org.apache.isis.core.metamodel.services.container.query.QueryCardinality;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession5;
+import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryContext;
 import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryFindUsingApplibQueryProcessor;
 
 /**
@@ -85,7 +85,7 @@ public class PersistenceQueryFindUsingApplibQueryDefault extends PersistenceQuer
     }
 
     @Override
-    public Can<ManagedObject> execute(JdoPersistenceSession5 persistenceSession) {
-        return new PersistenceQueryFindUsingApplibQueryProcessor(persistenceSession).process(this);
+    public Can<ManagedObject> execute(PersistenceQueryContext queryContext) {
+        return new PersistenceQueryFindUsingApplibQueryProcessor().process(queryContext, this);
     }
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TransactionalCommandProcessor.java
similarity index 63%
copy from persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
copy to persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TransactionalCommandProcessor.java
index fa0e991..278d182 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/persistence/queries/PersistenceQueryProcessor.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TransactionalCommandProcessor.java
@@ -16,14 +16,21 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.integration.persistence.queries;
+package org.apache.isis.persistence.jdo.integration.transaction;
 
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommand;
+import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryContext;
 import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQuery;
 
-public interface PersistenceQueryProcessor<T extends PersistenceQuery> {
+public interface TransactionalCommandProcessor {
+
+    void executeWithinTransaction(PersistenceCommand persistenceCommand);
+
+    Can<ManagedObject> executeWithinTransaction(PersistenceQueryContext queryContext, PersistenceQuery persistenceQuery);
+    
+    //void createObject(ManagedObject adapter);
+    //void deleteObject(ManagedObject adapter);
     
-    Can<ManagedObject> process(T query);
 }
-
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TxManagerInternalFactory.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TxManagerInternalFactory.java
index def07bd..4b11272 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TxManagerInternalFactory.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/TxManagerInternalFactory.java
@@ -20,14 +20,13 @@ package org.apache.isis.persistence.jdo.integration.transaction;
 
 import org.apache.isis.core.interaction.session.InteractionTracker;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommandQueue;
 import org.apache.isis.persistence.jdo.provider.persistence.HasPersistenceManager;
 
 import lombok.val;
 
 public class TxManagerInternalFactory {
 
-    public static PersistenceCommandQueue newCommandQueue(
+    public static TransactionalCommandProcessor newCommandQueue(
             MetaModelContext mmc,
             HasPersistenceManager pmProvider) {
         
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_CommandQueue.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_CommandQueue.java
new file mode 100644
index 0000000..87d8378
--- /dev/null
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_CommandQueue.java
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.persistence.jdo.integration.transaction;
+
+import java.util.function.Consumer;
+
+import javax.annotation.Nullable;
+
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.collections._Inbox;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.persistence.jdo.integration.persistence.command.CreateObjectCommand;
+import org.apache.isis.persistence.jdo.integration.persistence.command.DeleteObjectCommand;
+import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommand;
+
+import lombok.NonNull;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+final class _CommandQueue {
+
+    private final _Inbox<PersistenceCommand> persistenceCommands = new _Inbox<>();
+
+    /**
+     * Add the non-null command to the list of commands to execute at the end of
+     * the transaction.
+     */
+    public void append(final @Nullable PersistenceCommand command) {
+        if (command == null) {
+            return;
+        }
+
+        final ManagedObject entity = command.getEntity();
+
+        // Destroys are ignored when preceded by a create, or another destroy
+        if (command instanceof DeleteObjectCommand) {
+            if (alreadyHasCreate(entity)) {
+                removeCreate(entity);
+                if (log.isDebugEnabled()) {
+                    log.debug("ignored both create and destroy command {}", command);
+                }
+                return;
+            }
+
+            if (alreadyHasDestroy(entity)) {
+                if (log.isDebugEnabled()) {
+                    log.debug("ignored command {} as command already recorded", command);
+                }
+                return;
+            }
+        }
+
+        log.debug("add command {}", command);
+        persistenceCommands.add(command);
+    }
+    
+    public void drain(final @NonNull Consumer<Can<PersistenceCommand>> onMoreCommands) {
+
+        //
+        // it's possible that in executing these commands that more will be created.
+        // so we keep flushing until no more are available (ISIS-533)
+        //
+        // this is a do...while rather than a while... just for backward compatibility
+        // with previous algorithm that always went through the execute phase at least once.
+        //
+        do {
+            // this algorithm ensures that we never execute the same command twice,
+            // and also allow new commands to be added to end
+            val pc_snapshot = persistenceCommands.snapshotThenClear();
+
+            if(!pc_snapshot.isEmpty()) {
+                try {
+                    
+                    onMoreCommands.accept(pc_snapshot);
+                    
+                } catch (RuntimeException ex) {
+                    // if there's an exception, we want to make sure that
+                    // all commands are cleared and propagate
+                    persistenceCommands.clear();
+                    throw ex;
+                }
+            }
+        } while(!persistenceCommands.isEmpty());
+
+    }
+    
+    @Override
+    public String toString() {
+        return String.format("CommandQueue[%s]", persistenceCommands.snapshot());
+    }
+    
+    // -- HELPER
+    
+    private boolean alreadyHasCommand(final Class<?> commandClass, final ManagedObject onObject) {
+        return getCommand(commandClass, onObject) != null;
+    }
+
+    private boolean alreadyHasCreate(final ManagedObject onObject) {
+        return alreadyHasCommand(CreateObjectCommand.class, onObject);
+    }
+
+    private boolean alreadyHasDestroy(final ManagedObject onObject) {
+        return alreadyHasCommand(DeleteObjectCommand.class, onObject);
+    }
+
+    private PersistenceCommand getCommand(final Class<?> commandClass, final ManagedObject entity) {
+        for (final PersistenceCommand command : persistenceCommands.snapshot()) {
+            if (command.getEntity().equals(entity)) {
+                if (commandClass.isAssignableFrom(command.getClass())) {
+                    return command;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void removeCommand(final Class<?> commandClass, final ManagedObject onObject) {
+        final PersistenceCommand toDelete = getCommand(commandClass, onObject);
+        persistenceCommands.remove(toDelete);
+    }
+
+    private void removeCreate(final ManagedObject onObject) {
+        removeCommand(CreateObjectCommand.class, onObject);
+    }
+    
+
+    
+}
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_Tx.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_Tx.java
index 9f4aa80..0719c7f 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_Tx.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_Tx.java
@@ -22,6 +22,7 @@ package org.apache.isis.persistence.jdo.integration.transaction;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 
+import javax.annotation.Nullable;
 import javax.enterprise.inject.Vetoed;
 
 import org.apache.isis.applib.annotation.Programmatic;
@@ -33,20 +34,17 @@ import org.apache.isis.applib.services.xactn.TransactionState;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.exceptions.IsisException;
 import org.apache.isis.commons.internal.assertions._Assert;
-import org.apache.isis.commons.internal.collections._Inbox;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.interaction.session.InteractionTracker;
-import org.apache.isis.core.metamodel.commons.ToString;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.transaction.integration.IsisTransactionFlushException;
 import org.apache.isis.core.transaction.integration.IsisTransactionManagerException;
 import org.apache.isis.persistence.jdo.integration.persistence.JdoPersistenceSession;
-import org.apache.isis.persistence.jdo.integration.persistence.command.CreateObjectCommand;
-import org.apache.isis.persistence.jdo.integration.persistence.command.DeleteObjectCommand;
 import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommand;
 
 import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
@@ -62,7 +60,7 @@ import lombok.extern.log4j.Log4j2;
  * the {@link _TxManagerInternal} to ensure that the underlying persistence
  * mechanism (for example, the <tt>ObjectStore</tt>) is also committed.
  */
-@Vetoed @Log4j2
+@Vetoed @Log4j2 @ToString
 class _Tx implements Transaction {
 
     public enum State {
@@ -154,12 +152,15 @@ class _Tx implements Transaction {
     @Getter @Programmatic
     private final TransactionId id;
 
-    private final _Inbox<PersistenceCommand> persistenceCommands = new _Inbox<>();
-
+    @ToString.Exclude
     private final _TxHelper txHelper;
     
+    private final _CommandQueue commandQueue;
+    
+    @ToString.Exclude
     private final InteractionTracker isisInteractionTracker;
 
+    @ToString.Exclude
     private final Can<TransactionScopeListener> transactionScopeListeners;
 
     private IsisException abortCause;
@@ -173,6 +174,7 @@ class _Tx implements Transaction {
         id = TransactionId.of(interactionId, sequence);
         
         this.txHelper = txHelper;
+        this.commandQueue = new _CommandQueue();
         this.isisInteractionTracker = mmc.getServiceRegistry().lookupServiceElseFail(InteractionTracker.class);
         this.transactionScopeListeners = mmc.getServiceRegistry().select(TransactionScopeListener.class);
 
@@ -192,7 +194,10 @@ class _Tx implements Transaction {
 
     @Getter
     private State state;
-    private void setState(final State state) {
+    private void setState(final @NonNull State state) {
+        if(this.state == state) {
+            return;
+        }
         this.state = state;
         if(state.isComplete()) {
             countDownLatch.countDown();
@@ -206,12 +211,10 @@ class _Tx implements Transaction {
             return TransactionState.NONE;
         }
 
-        final TransactionState transactionState = getState().getTransactionState();
-        if (transactionState == null) {
-            return TransactionState.NONE;
-        }
-
-        return transactionState;
+        val transactionState = getState().getTransactionState();
+        return transactionState == null
+                ? TransactionState.NONE
+                : transactionState;
     }
 
     // -- commands
@@ -220,65 +223,8 @@ class _Tx implements Transaction {
      * Add the non-null command to the list of commands to execute at the end of
      * the transaction.
      */
-    public void addCommand(final PersistenceCommand command) {
-        if (command == null) {
-            return;
-        }
-
-        final ManagedObject onObject = command.getEntity();
-
-        // Destroys are ignored when preceded by a create, or another destroy
-        if (command instanceof DeleteObjectCommand) {
-            if (alreadyHasCreate(onObject)) {
-                removeCreate(onObject);
-                if (log.isDebugEnabled()) {
-                    log.debug("ignored both create and destroy command {}", command);
-                }
-                return;
-            }
-
-            if (alreadyHasDestroy(onObject)) {
-                if (log.isDebugEnabled()) {
-                    log.debug("ignored command {} as command already recorded", command);
-                }
-                return;
-            }
-        }
-
-        log.debug("add command {}", command);
-        persistenceCommands.add(command);
-    }
-
-    private boolean alreadyHasCommand(final Class<?> commandClass, final ManagedObject onObject) {
-        return getCommand(commandClass, onObject) != null;
-    }
-
-    private boolean alreadyHasCreate(final ManagedObject onObject) {
-        return alreadyHasCommand(CreateObjectCommand.class, onObject);
-    }
-
-    private boolean alreadyHasDestroy(final ManagedObject onObject) {
-        return alreadyHasCommand(DeleteObjectCommand.class, onObject);
-    }
-
-    private PersistenceCommand getCommand(final Class<?> commandClass, final ManagedObject onObject) {
-        for (final PersistenceCommand command : persistenceCommands.snapshot()) {
-            if (command.getEntity().equals(onObject)) {
-                if (commandClass.isAssignableFrom(command.getClass())) {
-                    return command;
-                }
-            }
-        }
-        return null;
-    }
-
-    private void removeCommand(final Class<?> commandClass, final ManagedObject onObject) {
-        final PersistenceCommand toDelete = getCommand(commandClass, onObject);
-        persistenceCommands.remove(toDelete);
-    }
-
-    private void removeCreate(final ManagedObject onObject) {
-        removeCommand(CreateObjectCommand.class, onObject);
+    public void addCommand(final @Nullable PersistenceCommand command) {
+        commandQueue.append(command);
     }
 
     // -- flush
@@ -326,33 +272,7 @@ class _Tx implements Transaction {
      * </table>
      */
     private void flushCommands() {
-
-        //
-        // it's possible that in executing these commands that more will be created.
-        // so we keep flushing until no more are available (ISIS-533)
-        //
-        // this is a do...while rather than a while... just for backward compatibility
-        // with previous algorithm that always went through the execute phase at least once.
-        //
-        do {
-            // this algorithm ensures that we never execute the same command twice,
-            // and also allow new commands to be added to end
-            val pc_snapshot = persistenceCommands.snapshotThenClear();
-
-            if(!pc_snapshot.isEmpty()) {
-                try {
-                    
-                    txHelper.execute(pc_snapshot);
-                    
-                } catch (RuntimeException ex) {
-                    // if there's an exception, we want to make sure that
-                    // all commands are cleared and propagate
-                    persistenceCommands.clear();
-                    throw ex;
-                }
-            }
-        } while(!persistenceCommands.isEmpty());
-
+        commandQueue.drain(txHelper::execute);
     }
     
     private void flushTransaction() {
@@ -468,19 +388,6 @@ class _Tx implements Transaction {
         setState(State.IN_PROGRESS);
         clearAbortCause();
     }
-
-    // -- toString
-
-    @Override
-    public String toString() {
-        return appendTo(new ToString(this)).toString();
-    }
-
-    private ToString appendTo(final ToString str) {
-        str.append("state", state);
-        str.append("commands", persistenceCommands.size());
-        return str;
-    }
     
 
 }
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxHelper.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxHelper.java
index 4a96eab..75ac85b 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxHelper.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxHelper.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.persistence.jdo.integration.transaction;
 
-import java.util.List;
-
 import javax.jdo.PersistenceManager;
 
 import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommand;
@@ -79,14 +77,14 @@ interface _TxHelper extends HasPersistenceManager {
         }
     }
     
-    default void execute(final List<PersistenceCommand> commands) {
+    default void execute(final Iterable<PersistenceCommand> commands) {
 
         // previously we used to check that there were some commands, and skip processing otherwise.
         // we no longer do that; it could be (is quite likely) that DataNucleus has some dirty objects anyway that
         // don't have commands wrapped around them...
 
         val pm = getPersistenceManager();
-        for (final PersistenceCommand command : commands) {
+        for (val command : commands) {
             command.execute(pm);
         }
         pm.flush();
diff --git a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxManagerInternal.java b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxManagerInternal.java
index 3ce664b..ce4fc3f 100644
--- a/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxManagerInternal.java
+++ b/persistence/jdo/integration/src/main/java/org/apache/isis/persistence/jdo/integration/transaction/_TxManagerInternal.java
@@ -25,14 +25,17 @@ import javax.enterprise.inject.Vetoed;
 
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.iactn.InteractionContext;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.exceptions.IsisException;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.transaction.integration.IsisTransactionAspectSupport;
 import org.apache.isis.core.transaction.integration.IsisTransactionManagerException;
 import org.apache.isis.core.transaction.integration.IsisTransactionObject;
 import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommand;
-import org.apache.isis.persistence.jdo.integration.persistence.command.PersistenceCommandQueue;
+import org.apache.isis.persistence.jdo.integration.persistence.queries.PersistenceQueryContext;
+import org.apache.isis.persistence.jdo.integration.persistence.query.PersistenceQuery;
 import org.apache.isis.persistence.jdo.provider.persistence.HasPersistenceManager;
 
 import lombok.val;
@@ -41,7 +44,7 @@ import lombok.extern.log4j.Log4j2;
 @Vetoed @Log4j2
 class _TxManagerInternal 
 implements 
-    PersistenceCommandQueue {
+    TransactionalCommandProcessor {
 
     // -- constructor, fields
 
@@ -307,7 +310,6 @@ implements
             }
         }
 
-
     }
 
     public void abortTransaction(IsisTransactionObject txObject) {
@@ -320,11 +322,26 @@ implements
     }
 
     @Override
-    public void addCommand(PersistenceCommand command) {
-        val transaction = getCurrentTransaction();
-        if (transaction != null && command != null) {
-            transaction.addCommand(command);
-        }
+    public void executeWithinTransaction(PersistenceCommand command) {
+        mmc.getTransactionService().executeWithinTransaction(()->{
+        
+            val transaction = getCurrentTransaction();
+            if (transaction != null && command != null) {
+                transaction.addCommand(command);
+            }
+            
+        })
+        .nullableOrElseFail();
+    }
+    
+    @Override
+    public Can<ManagedObject> executeWithinTransaction(
+            PersistenceQueryContext queryContext, 
+            PersistenceQuery persistenceQuery) {
+        final Can<ManagedObject> instances = mmc.getTransactionService().executeWithinTransaction(
+                        ()->persistenceQuery.execute(queryContext) )
+                .orElseFail();
+        return instances;
     }
 
     // -- HELPER