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 2022/04/16 09:04:15 UTC

[isis] branch master updated: ISIS-3010: provide concrete CommandJdo/Jpa (reverting JPA annotation experiments)

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


The following commit(s) were added to refs/heads/master by this push:
     new 9f371f80c2 ISIS-3010: provide concrete CommandJdo/Jpa (reverting JPA annotation experiments)
9f371f80c2 is described below

commit 9f371f80c264aae6e9bf80b802befd5e832d9773
Author: andi-huber <ah...@apache.org>
AuthorDate: Sat Apr 16 11:04:07 2022 +0200

    ISIS-3010: provide concrete CommandJdo/Jpa (reverting JPA annotation
    experiments)
---
 core/pom.xml                                       |   5 -
 .../ExposePersistedCommands_commands.java          |   4 +-
 extensions/core/command-log/applib/pom.xml         |   5 +
 .../commandlog/applib/command/CommandLog.java      | 284 ++++-----------------
 ...gRepository.java => ICommandLogRepository.java} |   2 +-
 .../command/mixins/CommandLog_childCommands.java   |   4 +-
 .../command/mixins/CommandLog_siblingCommands.java |   4 +-
 extensions/core/command-log/jdo/pom.xml            |  10 -
 .../commandlog/jdo/IsisModuleExtCommandLogJdo.java |   9 +-
 .../commandlog/jdo/entities/CommandJdo.java        | 114 +++++++--
 .../jdo/entities/CommandJdoRepository.java         |   4 +-
 .../commandlog/jpa/IsisModuleExtCommandLogJpa.java |   6 +-
 .../commandlog/jpa/entities/CommandJpa.java        | 234 ++++++++++++++++-
 .../jpa/entities/CommandJpaRepository.java         |   5 +-
 .../restapi/CommandRetrievalOnPrimaryService.java  |   6 +-
 .../primary/ui/CommandReplayOnPrimaryService.java  |   6 +-
 .../jobcallables/ReplicateAndRunCommands.java      |   4 +-
 .../secondary/mixins/CommandLog_replayQueue.java   |   4 +-
 .../ui/CommandReplayOnSecondaryService.java        |   4 +-
 extensions/pom.xml                                 |   5 -
 20 files changed, 399 insertions(+), 320 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 44acfcb5be..b3b007a644 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -1549,11 +1549,6 @@
 								<artifactId>datanucleus-api-jdo</artifactId>
 								<version>${datanucleus-api-jdo.version}</version>
 							</dependency>
-							<dependency>
-								<groupId>org.datanucleus</groupId>
-								<artifactId>datanucleus-api-jpa</artifactId>
-								<version>${datanucleus-api-jpa.version}</version>
-							</dependency>
 							<dependency>
 								<groupId>org.datanucleus</groupId>
 								<artifactId>datanucleus-jodatime</artifactId>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java
index ccef2132fb..eb6bbdb125 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java
@@ -25,7 +25,7 @@ import javax.inject.Inject;
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 
 import lombok.RequiredArgsConstructor;
 
@@ -43,6 +43,6 @@ public class ExposePersistedCommands_commands {
         return commandModelRepository.findCompleted();
     }
 
-    @Inject CommandLogRepository<? extends ICommandLog> commandModelRepository;
+    @Inject ICommandLogRepository<? extends ICommandLog> commandModelRepository;
 }
 //end::class[]
diff --git a/extensions/core/command-log/applib/pom.xml b/extensions/core/command-log/applib/pom.xml
index 57ba4a07a5..86be82e515 100644
--- a/extensions/core/command-log/applib/pom.xml
+++ b/extensions/core/command-log/applib/pom.xml
@@ -65,6 +65,11 @@
             <groupId>org.apache.isis.core</groupId>
             <artifactId>isis-core-runtimeservices</artifactId>
         </dependency>
+        
+		<dependency>
+		    <groupId>org.datanucleus</groupId>
+		    <artifactId>javax.jdo</artifactId>
+		</dependency>
 
         <!-- Testing -->
 
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLog.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLog.java
index 3f5792ba85..c48f42b061 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLog.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLog.java
@@ -28,12 +28,8 @@ import java.util.Optional;
 import java.util.UUID;
 import java.util.function.Consumer;
 
-import javax.persistence.Basic;
 import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.Id;
-import javax.persistence.Lob;
+import javax.persistence.MappedSuperclass;
 import javax.persistence.Transient;
 
 import org.springframework.context.event.EventListener;
@@ -55,7 +51,6 @@ import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandOutcomeHandler;
 import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
 import org.apache.isis.applib.services.tablecol.TableColumnOrderForCollectionTypeAbstract;
-import org.apache.isis.applib.types.MemberIdentifierType;
 import org.apache.isis.applib.util.TitleBuffer;
 import org.apache.isis.commons.functional.Try;
 import org.apache.isis.commons.internal.base._Strings;
@@ -66,9 +61,7 @@ import org.apache.isis.extensions.commandlog.applib.util.StringUtils;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.MapDto;
 
-import lombok.Getter;
 import lombok.NoArgsConstructor;
-import lombok.Setter;
 import lombok.val;
 
 /**
@@ -82,172 +75,7 @@ import lombok.val;
  * Note that this class doesn't subclass from {@link Command} ({@link Command}
  * is not an interface).
  */
-/*FIXME convert to JPA
-@javax.jdo.annotations.PersistenceCapable(
-        identityType=IdentityType.APPLICATION,
-        schema = "isisExtensionsCommandLog",
-        table = "Command")
-@javax.jdo.annotations.Queries( {
-    @javax.jdo.annotations.Query(
-            name="findByInteractionIdStr",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE interactionIdStr == :interactionIdStr "),
-    @javax.jdo.annotations.Query(
-            name="findByParent",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE parent == :parent "),
-    @javax.jdo.annotations.Query(
-            name="findCurrent",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE completedAt == null "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findCompleted",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE completedAt != null "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findRecentByTarget",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE target == :target "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampBetween",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp >= :from "
-                    + "&& timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampAfter",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp >= :from "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampBefore",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTarget",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE target == :target "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampBetween",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE timestamp >= :from "
-                    + "&&    timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampAfter",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE timestamp >= :from "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampBefore",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="find",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findRecentByUsername",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE username == :username "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findFirst",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE startedAt   != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp ASC "
-                    + "RANGE 0,2"),
-        // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
-        // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-    @javax.jdo.annotations.Query(
-            name="findSince",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE timestamp > :timestamp "
-                    + "   && startedAt != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp ASC"),
-    // most recent (replayed) command previously replicated from primary to
-    // secondary.  This should always exist except for the very first times
-    // (after restored the prod DB to secondary).
-    @javax.jdo.annotations.Query(
-            name="findMostRecentReplayed",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE (replayState == 'OK' || replayState == 'FAILED') "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,2"), // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
-                                    // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-    // the most recent completed command, as queried on the
-    // secondary, corresponding to the last command run on primary before the
-    // production database was restored to the secondary
-    @javax.jdo.annotations.Query(
-            name="findMostRecentCompleted",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE startedAt   != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,2"),
-        // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
-        // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-    @javax.jdo.annotations.Query(
-            name="findNotYetReplayed",
-            value="SELECT "
-                    + "FROM " + CommandEntity.FQCN
-                    + " WHERE replayState == 'PENDING' "
-                    + "ORDER BY this.timestamp ASC "
-                    + "RANGE 0,10"),    // same as batch size
-//        @javax.jdo.annotations.Query(
-//                name="findReplayableInErrorMostRecent",
-//                value="SELECT "
-//                        + "FROM " + CommandJdo.FQCN
-//                        + " WHERE replayState == 'FAILED' "
-//                        + "ORDER BY this.timestamp DESC "
-//                        + "RANGE 0,2"),
-//    @javax.jdo.annotations.Query(
-//            name="findReplayableMostRecentStarted",
-//            value="SELECT "
-//                    + "FROM " + CommandJdo.FQCN
-//                    + " WHERE replayState = 'PENDING' "
-//                    + "ORDER BY this.timestamp DESC "
-//                    + "RANGE 0,20"),
-})
-@javax.jdo.annotations.Indices({
-        @javax.jdo.annotations.Index(name = "CommandJdo__startedAt__timestamp__IDX", members = { "startedAt", "timestamp" }),
-        @javax.jdo.annotations.Index(name = "CommandJdo__timestamp__IDX", members = { "timestamp" }),
-//        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__timestamp__startedAt_IDX", members = { "replayState", "timestamp", "startedAt"}),
-//        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__startedAt__completedAt_IDX", members = {"startedAt", "replayState", "completedAt"}),
-})
-*/
-@Entity
+@MappedSuperclass
 @DomainObject(
         logicalTypeName = CommandLog.LOGICAL_TYPE_NAME,
         editing = Editing.DISABLED
@@ -261,14 +89,12 @@ import lombok.val;
 )
 //@Log4j2
 @NoArgsConstructor
-public class CommandLog
+public abstract class CommandLog
 implements
     ICommandLog,
     DomainChangeRecord {
 
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogApplib.NAMESPACE + ".CommandLogEntity";
-
-    protected final static String FQCN = "org.apache.isis.extensions.commandlog.applib.command.CommandLogEntity";
+    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogApplib.NAMESPACE + ".CommandLog";
 
     /**
      * Intended for use on primary system.
@@ -373,49 +199,46 @@ implements
      * to persist if using h2 (perhaps would need to be mapped differently).
      * @see <a href="https://www.datanucleus.org/products/accessplatform/jdo/mapping.html#_other_types">www.datanucleus.org</a>
      */
-    @Id
-    @Column(nullable=false, name = "interactionId", length = 36)
     @Property(domainEvent = InteractionIdDomainEvent.class)
     @PropertyLayout(named = "Interaction Id")
-    @Getter @Setter
-    private String interactionIdStr;
+    public abstract String getInteractionIdStr();
+    public abstract void setInteractionIdStr(String interactionIdStr);
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Programmatic
     @Override
     public UUID getInteractionId() {return UUID.fromString(getInteractionIdStr());}
 
 
     public static class UsernameDomainEvent extends PropertyDomainEvent<String> { }
-    @Column(nullable=false, length = 50)
     @Property(domainEvent = UsernameDomainEvent.class)
-    @Getter @Setter
-    private String username;
+    @Override
+    public abstract String getUsername();
+    public abstract void setUsername(String userName);
 
 
     public static class TimestampDomainEvent extends PropertyDomainEvent<Timestamp> { }
-    @Column(nullable=false)
     @Property(domainEvent = TimestampDomainEvent.class)
-    @Getter @Setter
-    private Timestamp timestamp;
+    @Override
+    public abstract Timestamp getTimestamp();
+    public abstract void setTimestamp(Timestamp timestamp);
 
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Override
     public ChangeType getType() {
         return ChangeType.COMMAND;
     }
 
-
     public static class ReplayStateDomainEvent extends PropertyDomainEvent<ReplayState> { }
     /**
      * For a replayed command, what the outcome was.
      */
-    @Column(nullable=true, length=10)
     @Property(domainEvent = ReplayStateDomainEvent.class)
-    @Getter @Setter
-    private ReplayState replayState;
-
+    @Override
+    public abstract ReplayState getReplayState();
 
     public static class ReplayStateFailureReasonDomainEvent extends PropertyDomainEvent<ReplayState> { }
     /**
@@ -424,40 +247,40 @@ implements
     @Column(nullable=true, length=255)
     @Property(domainEvent = ReplayStateFailureReasonDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5)
-    @Getter @Setter
-    private String replayStateFailureReason;
+    public abstract String getReplayStateFailureReason();
+    public abstract void setReplayStateFailureReason(String replayStateFailureReason);
     @MemberSupport public boolean hideReplayStateFailureReason() {
         return getReplayState() == null || !getReplayState().isFailed();
     }
 
-
     public static class ParentDomainEvent extends PropertyDomainEvent<Command> { }
-    @Column(name="parentId", nullable=true)
     @Property(domainEvent = ParentDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES)
-    @Getter @Setter
-    private CommandLog parent;
-
+    public abstract CommandLog getParent();
+    public abstract void setParent(CommandLog parent);
 
     public static class TargetDomainEvent extends PropertyDomainEvent<String> { }
-    @Column(nullable=true, length = 2000, name="target")
+    @Override
     @Property(domainEvent = TargetDomainEvent.class)
     @PropertyLayout(named = "Object")
-    @Getter @Setter
-    private Bookmark target;
+    public abstract Bookmark getTarget();
+    public abstract void setTarget(Bookmark target);
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     public String getTargetStr() {
         return Optional.ofNullable(getTarget()).map(Bookmark::toString).orElse(null);
     }
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Override
     public String getTargetMember() {
         return getCommandDto().getMember().getLogicalMemberIdentifier();
     }
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Property(domainEvent = TargetDomainEvent.class)
     @PropertyLayout(named = "Member")
     public String getLocalMember() {
@@ -466,35 +289,30 @@ implements
     }
 
     public static class LogicalMemberIdentifierDomainEvent extends PropertyDomainEvent<String> { }
+    @Override
     @Property(domainEvent = LogicalMemberIdentifierDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES)
-    @Column(nullable=false, length = MemberIdentifierType.Meta.MAX_LEN)
-    @Getter @Setter
-    private String logicalMemberIdentifier;
-
+    public abstract String getLogicalMemberIdentifier();
+    public abstract void setLogicalMemberIdentifier(String logicalMemberIdentifier);
 
     public static class CommandDtoDomainEvent extends PropertyDomainEvent<CommandDto> { }
-    @Lob @Basic(fetch=FetchType.LAZY)
-    @Column(nullable=true, columnDefinition="CLOB")
     @Property(domainEvent = CommandDtoDomainEvent.class)
     @PropertyLayout(multiLine = 9)
-    @Getter @Setter
-    private CommandDto commandDto;
-
+    @Override
+    public abstract CommandDto getCommandDto();
+    public abstract void setCommandDto(CommandDto commandDto);
 
     public static class StartedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
-    @Column(nullable=true)
+    @Override
     @Property(domainEvent = StartedAtDomainEvent.class)
-    @Getter @Setter
-    private Timestamp startedAt;
-
+    public abstract Timestamp getStartedAt();
+    public abstract void setStartedAt(Timestamp startedAt);
 
     public static class CompletedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
-    @Column(nullable=true)
+    @Override
     @Property(domainEvent = CompletedAtDomainEvent.class)
-    @Getter @Setter
-    private Timestamp completedAt;
-
+    public abstract Timestamp getCompletedAt();
+    public abstract void setCompletedAt(Timestamp completedAt);
 
     public static class DurationDomainEvent extends PropertyDomainEvent<BigDecimal> { }
     /**
@@ -504,6 +322,7 @@ implements
      * Populated only if it has {@link #getCompletedAt() completed}.
      */
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @javax.validation.constraints.Digits(integer=5, fraction=3)
     @Property(domainEvent = DurationDomainEvent.class)
     public BigDecimal getDuration() {
@@ -513,6 +332,7 @@ implements
 
     public static class IsCompleteDomainEvent extends PropertyDomainEvent<Boolean> { }
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Property(domainEvent = IsCompleteDomainEvent.class)
     @PropertyLayout(hidden = Where.OBJECT_FORMS)
     public boolean isComplete() {
@@ -522,6 +342,7 @@ implements
 
     public static class ResultSummaryDomainEvent extends PropertyDomainEvent<String> { }
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Property(domainEvent = ResultSummaryDomainEvent.class)
     @PropertyLayout(hidden = Where.OBJECT_FORMS, named = "Result")
     public String getResultSummary() {
@@ -538,13 +359,12 @@ implements
         }
     }
 
-
     public static class ResultDomainEvent extends PropertyDomainEvent<String> { }
-    @Column(nullable=true, length = 2000, name="result")
+    @Override
     @Property(domainEvent = ResultDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES, named = "Result Bookmark")
-    @Getter @Setter
-    private Bookmark result;
+    public abstract Bookmark getResult();
+    public abstract void setResult(Bookmark result);
 
     public static class ExceptionDomainEvent extends PropertyDomainEvent<String> { }
     /**
@@ -554,21 +374,20 @@ implements
      * Not part of the applib API, because the default implementation is not persistent
      * and so there's no object that can be accessed to be annotated.
      */
-    @Lob @Basic(fetch=FetchType.LAZY)
-    @Column(nullable=true, columnDefinition="CLOB")
+    @Override
     @Property(domainEvent = ExceptionDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5, named = "Exception (if any)")
-    @Getter
-    private String exception;
-    public void setException(final String exception) {
-        this.exception = exception;
-    }
+    public abstract String getException();
+    public abstract void setException(final String exception);
+    @Transient
+    @javax.jdo.annotations.NotPersistent
     public void setException(final Throwable exception) {
         setException(_Exceptions.asStacktrace(exception));
     }
 
     public static class IsCausedExceptionDomainEvent extends PropertyDomainEvent<Boolean> { }
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Property(domainEvent = IsCausedExceptionDomainEvent.class)
     @PropertyLayout(hidden = Where.OBJECT_FORMS)
     public boolean isCausedException() {
@@ -576,12 +395,14 @@ implements
     }
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Override
     public String getPreValue() {
         return null;
     }
 
     @Transient
+    @javax.jdo.annotations.NotPersistent
     @Override
     public String getPostValue() {
         return null;
@@ -631,6 +452,7 @@ implements
         };
     }
 
+
     @Service
     @javax.annotation.Priority(PriorityPrecedence.LATE - 10) // before the framework's own default.
     public static class TableColumnOrderDefault extends TableColumnOrderForCollectionTypeAbstract<CommandLog> {
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLogRepository.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/ICommandLogRepository.java
similarity index 98%
rename from extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLogRepository.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/ICommandLogRepository.java
index 21cc1aa9f5..849e3a1533 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/CommandLogRepository.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/ICommandLogRepository.java
@@ -32,7 +32,7 @@ import org.apache.isis.schema.cmd.v2.CommandsDto;
 
 import lombok.Getter;
 
-public interface CommandLogRepository<C extends ICommandLog> {
+public interface ICommandLogRepository<C extends ICommandLog> {
 
     Optional<C> findByInteractionId(UUID interactionId);
 
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_childCommands.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_childCommands.java
index 90d8e0d372..a9e538ee7c 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_childCommands.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_childCommands.java
@@ -26,7 +26,7 @@ import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 
 import lombok.RequiredArgsConstructor;
 
@@ -52,6 +52,6 @@ public class CommandLog_childCommands {
     }
 
     @javax.inject.Inject
-    private CommandLogRepository<CommandLog> commandLogRepository;
+    private ICommandLogRepository<CommandLog> commandLogRepository;
 
 }
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_siblingCommands.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_siblingCommands.java
index 099e9dc81f..9e81791c23 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_siblingCommands.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/command/mixins/CommandLog_siblingCommands.java
@@ -26,7 +26,7 @@ import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.annotation.MemberSupport;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 
 import lombok.RequiredArgsConstructor;
 
@@ -58,6 +58,6 @@ public class CommandLog_siblingCommands {
 
 
     @javax.inject.Inject
-    private CommandLogRepository<CommandLog> commandLogRepository;
+    private ICommandLogRepository<CommandLog> commandLogRepository;
 
 }
diff --git a/extensions/core/command-log/jdo/pom.xml b/extensions/core/command-log/jdo/pom.xml
index dbe393a434..430b875cc7 100644
--- a/extensions/core/command-log/jdo/pom.xml
+++ b/extensions/core/command-log/jdo/pom.xml
@@ -57,16 +57,6 @@
             <groupId>org.apache.isis.persistence</groupId>
 			<artifactId>isis-persistence-jdo-datanucleus</artifactId>
         </dependency>
-        
-        <!-- this dependency must not be transitive, its sole purpose is 
-        to allow the DN enhancer to pick it up in order to process JPA annotations; 
-        and thats only required when building with an IDE (Eclipse) and not with Maven;
-        the Maven build has its own DN enhancer plugin, which also has its own dependencies declared -->
-     	<dependency>
-			<groupId>org.datanucleus</groupId>
-			<artifactId>datanucleus-api-jpa</artifactId>
-			<scope>provided</scope>
-		</dependency>
 
         <!-- Testing -->
 
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
index a45dfd4f18..54d88b36bf 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
@@ -22,6 +22,7 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
+import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
@@ -35,14 +36,14 @@ import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAb
 @Configuration
 @Import({
         // @DomainService's
-        CommandJdoRepository.class
-        , CommandServiceMenu.class
+        CommandServiceMenu.class,
 
         // @Service's
-        , CommandJdo.TableColumnOrderDefault.class
+        CommandJdoRepository.class,
+        CommandLog.TableColumnOrderDefault.class,
 
         // entities
-        , CommandJdo.class
+        CommandJdo.class
 })
 @ComponentScan(
         basePackageClasses= {
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java
index 98e78087cf..9afbe85be0 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java
@@ -18,33 +18,28 @@
  */
 package org.apache.isis.extensions.commandlog.jdo.entities;
 
-import javax.persistence.Entity;
-import javax.persistence.Table;
+import java.sql.Timestamp;
+
+import javax.jdo.annotations.IdentityType;
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.types.MemberIdentifierType;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
 import org.apache.isis.extensions.commandlog.applib.command.ReplayState;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+import lombok.Getter;
 import lombok.NoArgsConstructor;
+import lombok.Setter;
 
-/**
- * @deprecated use {@link CommandLog} instead
- */
-//@javax.jdo.annotations.PersistenceCapable(
-//        identityType = IdentityType.APPLICATION,
-//        schema = "isisExtensionsCommandLog",
-//        table = "Command")
-
-@Entity
-@Table(
+@javax.jdo.annotations.PersistenceCapable(
+        identityType = IdentityType.APPLICATION,
         schema = "isisExtensionsCommandLog",
-        name = "Command"
-)
-
+        table = "Command")
 @javax.jdo.annotations.Queries( {
     @javax.jdo.annotations.Query(
             name="findByInteractionIdStr",
@@ -204,18 +199,31 @@ import lombok.NoArgsConstructor;
 //        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__timestamp__startedAt_IDX", members = { "replayState", "timestamp", "startedAt"}),
 //        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__startedAt__completedAt_IDX", members = {"startedAt", "replayState", "completedAt"}),
 })
+
+//    @javax.jdo.annotations.Query(
+//            name="findReplayableInErrorMostRecent",
+//            value="SELECT "
+//                    + "FROM " + CommandJdo.FQCN
+//                    + " WHERE replayState == 'FAILED' "
+//                    + "ORDER BY this.timestamp DESC "
+//                    + "RANGE 0,2"),
+//    @javax.jdo.annotations.Query(
+//            name="findReplayableMostRecentStarted",
+//            value="SELECT "
+//                    + "FROM " + CommandJdo.FQCN
+//                    + " WHERE replayState = 'PENDING' "
+//                    + "ORDER BY this.timestamp DESC "
+//                    + "RANGE 0,20"),
 @DomainObject(
         logicalTypeName = CommandJdo.LOGICAL_TYPE_NAME,
         editing = Editing.DISABLED
 )
 //@Log4j2
-@Deprecated
 @NoArgsConstructor
 public class CommandJdo
 extends CommandLog {
 
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJdo.NAMESPACE + ".CommandJdo";
-
+    protected final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJdo.NAMESPACE + ".CommandJdo";
     protected final static String FQCN = "org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo";
 
     /**
@@ -227,7 +235,6 @@ extends CommandLog {
         super(command);
     }
 
-
     /**
      * Intended for use on secondary (replay) system.
      *
@@ -242,12 +249,69 @@ extends CommandLog {
         super(commandDto, replayState, targetIndex);
     }
 
-//    @Override
-//    @Id
-//    @Column(nullable=false, name = "interactionId", length = 36)
-//    public String getInteractionIdStr() {
-//        return super.getInteractionIdStr();
-//    }
+    @javax.jdo.annotations.PrimaryKey
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="false", name = "interactionId", length = 36)
+    @Getter @Setter
+    private String interactionIdStr;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="false", length = 50)
+    @Getter @Setter
+    private String username;
+
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @Getter @Setter
+    private Timestamp timestamp;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", length=10)
+    @Getter @Setter
+    private ReplayState replayState;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", length=255)
+    @Getter @Setter
+    private String replayStateFailureReason;
+
+    @javax.jdo.annotations.Column(name="parentId", allowsNull="true")
+    @Getter @Setter
+    private CommandLog parent;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="target")
+    @Getter @Setter
+    private Bookmark target;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
+    @Getter @Setter
+    private String logicalMemberIdentifier;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
+    @Getter @Setter
+    private CommandDto commandDto;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true")
+    @Getter @Setter
+    private Timestamp startedAt;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true")
+    @Getter @Setter
+    private Timestamp completedAt;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="result")
+    @Getter @Setter
+    private Bookmark result;
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
+    @Getter @Setter
+    private String exception;
 
 }
 
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java
index 1eec66efbb..0c1037569a 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java
@@ -45,8 +45,8 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.InteractionProvider;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 import org.apache.isis.extensions.commandlog.applib.command.ReplayState;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
 import org.apache.isis.persistence.jdo.applib.services.JdoSupportService;
@@ -70,7 +70,7 @@ import lombok.val;
 @RequiredArgsConstructor
 //@Log4j2
 public class CommandJdoRepository
-implements CommandLogRepository<CommandJdo> {
+implements ICommandLogRepository<CommandJdo> {
 
     @Inject final Provider<InteractionProvider> interactionProviderProvider;
     @Inject final Provider<RepositoryService> repositoryServiceProvider;
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
index 41a55ace2d..2e2c8eb415 100644
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
@@ -22,7 +22,7 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import org.apache.isis.extensions.commandlog.jpa.entities.CommandJpa;
+import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
 import org.apache.isis.extensions.commandlog.jpa.entities.CommandJpaRepository;
 
 /**
@@ -35,10 +35,8 @@ import org.apache.isis.extensions.commandlog.jpa.entities.CommandJpaRepository;
 //TODO        , CommandServiceMenu.class
 
         // @Service's
-        , CommandJpa.TableColumnOrderDefault.class
+        , CommandLog.TableColumnOrderDefault.class
 
-        // entities
-        , CommandJpa.class
 })
 @ComponentScan(
         basePackageClasses= {
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
index 2ac4cd903c..1384f59aba 100644
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
@@ -18,34 +18,189 @@
  */
 package org.apache.isis.extensions.commandlog.jpa.entities;
 
+import java.sql.Timestamp;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Lob;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.types.MemberIdentifierType;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
 import org.apache.isis.extensions.commandlog.applib.command.ReplayState;
 import org.apache.isis.extensions.commandlog.jpa.IsisModuleExtCommandLogJpa;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+import lombok.Getter;
 import lombok.NoArgsConstructor;
+import lombok.Setter;
 
-/**
- * @deprecated use {@link CommandLog} instead
- */
 @Entity
+@Table(
+        schema = "isisExtensionsCommandLog",
+        name = "Command",
+        indexes = {
+                @Index(name = "CommandJdo__startedAt__timestamp__IDX", columnList = "startedAt, timestamp" ),
+                @Index(name = "CommandJdo__timestamp__IDX", columnList = "timestamp"),
+//              @javax.jdo.annotations.Index(name = "CommandJdo__replayState__timestamp__startedAt_IDX", members = { "replayState", "timestamp", "startedAt"}),
+//              @javax.jdo.annotations.Index(name = "CommandJdo__replayState__startedAt__completedAt_IDX", members = {"startedAt", "replayState", "completedAt"}),
+        }
+)
+@NamedQueries({
+    @NamedQuery(
+            name="findByInteractionIdStr",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE cl.interactionIdStr = :interactionIdStr"),
+    @NamedQuery(
+            name="findByParent",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE parent = :parent "),
+    @NamedQuery(
+            name="findCurrent",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE completedAt is null "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findCompleted",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE completedAt is not null "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findRecentByTarget",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE target = :target "
+                    + "ORDER BY cl.timestamp DESC "
+                    + "RANGE 0,30"),
+    @NamedQuery(
+            name="findByTargetAndTimestampBetween",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE target = :target "
+                    + " AND timestamp >= :from "
+                    + " AND timestamp <= :to "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findByTargetAndTimestampAfter",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE target = :target "
+                    + " AND timestamp >= :from "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findByTargetAndTimestampBefore",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE target = :target "
+                    + " AND timestamp <= :to "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findByTarget",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE target = :target "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findByTimestampBetween",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE timestamp >= :from "
+                    + " AND  timestamp <= :to "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findByTimestampAfter",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE timestamp >= :from "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findByTimestampBefore",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE timestamp <= :to "
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="find",
+            query=CommandJpa.SELECT_FROM
+                    + "ORDER BY cl.timestamp DESC"),
+    @NamedQuery(
+            name="findRecentByUsername",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE username = :username "
+                    + "ORDER BY cl.timestamp DESC "
+                    + "RANGE 0,30"),
+    @NamedQuery(
+            name="findFirst",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE startedAt   is not null "
+                    + "   AND completedAt is not null "
+                    + "ORDER BY cl.timestamp ASC "
+                    + "RANGE 0,2"),
+        // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
+        // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
+    @NamedQuery(
+            name="findSince",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE timestamp > :timestamp "
+                    + "   AND startedAt is not null "
+                    + "   AND completedAt is not null "
+                    + "ORDER BY cl.timestamp ASC"),
+    // most recent (replayed) command previously replicated from primary to
+    // secondary.  This should always exist except for the very first times
+    // (after restored the prod DB to secondary).
+    @NamedQuery(
+            name="findMostRecentReplayed",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE (replayState == 'OK' || replayState == 'FAILED') "
+                    + "ORDER BY cl.timestamp DESC "
+                    + "RANGE 0,2"), // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
+                                    // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
+    // the most recent completed command, as queried on the
+    // secondary, corresponding to the last command run on primary before the
+    // production database was restored to the secondary
+    @NamedQuery(
+            name="findMostRecentCompleted",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE startedAt   is not null "
+                    + "   AND completedAt is not null "
+                    + "ORDER BY cl.timestamp DESC "
+                    + "RANGE 0,2"),
+        // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
+        // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
+    @NamedQuery(
+            name="findNotYetReplayed",
+            query=CommandJpa.SELECT_FROM
+                    + "WHERE replayState = 'PENDING' "
+                    + "ORDER BY cl.timestamp ASC "
+                    + "RANGE 0,10"),    // same as batch size
+})
+
+//    @javax.jdo.annotations.Query(
+//            name="findReplayableInErrorMostRecent",
+//            value="SELECT "
+//                    + "FROM " + CommandJdo.FQCN
+//                    + " WHERE replayState == 'FAILED' "
+//                    + "ORDER BY this.timestamp DESC "
+//                    + "RANGE 0,2"),
+//    @javax.jdo.annotations.Query(
+//            name="findReplayableMostRecentStarted",
+//            value="SELECT "
+//                    + "FROM " + CommandJdo.FQCN
+//                    + " WHERE replayState = 'PENDING' "
+//                    + "ORDER BY this.timestamp DESC "
+//                    + "RANGE 0,20"),
+
 @DomainObject(
         logicalTypeName = CommandJpa.LOGICAL_TYPE_NAME,
-        editing = Editing.DISABLED
-)
-//@Log4j2
-@Deprecated
+        editing = Editing.DISABLED)
 @NoArgsConstructor
-public class CommandJpa
-extends CommandLog {
+public class CommandJpa extends CommandLog {
 
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJpa.NAMESPACE + ".Command";
-    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jpa.entities.CommandJpa";
+    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJpa.NAMESPACE + ".CommandJpa";
+
+    public final static String SELECT_FROM = "SELECT cl FROM CommandJpa cl ";
 
     /**
      * Intended for use on primary system.
@@ -56,7 +211,6 @@ extends CommandLog {
         super(command);
     }
 
-
     /**
      * Intended for use on secondary (replay) system.
      *
@@ -71,5 +225,59 @@ extends CommandLog {
         super(commandDto, replayState, targetIndex);
     }
 
-}
+    @Id
+    @Column(nullable=false, name = "interactionId", length = 36)
+    @Getter @Setter
+    private String interactionIdStr;
+
+    @Column(nullable=false, length = 50)
+    @Getter @Setter
+    private String username;
+
+    @Column(nullable=false)
+    @Getter @Setter
+    private Timestamp timestamp;
 
+    @Column(nullable=true, length=10)
+    @Getter @Setter
+    private ReplayState replayState;
+
+    @Column(nullable=true, length=255)
+    @Getter @Setter
+    private String replayStateFailureReason;
+
+    @Column(name="parentId", nullable=true)
+    @Getter @Setter
+    private CommandLog parent;
+
+    @Column(nullable=true, length = 2000, name="target")
+    @Getter @Setter
+    private Bookmark target;
+
+    @Column(nullable=false, length = MemberIdentifierType.Meta.MAX_LEN)
+    @Getter @Setter
+    private String logicalMemberIdentifier;
+
+    @Lob @Basic(fetch=FetchType.LAZY)
+    @Column(nullable=true, columnDefinition="CLOB")
+    @Getter @Setter
+    private CommandDto commandDto;
+
+    @Column(nullable=true)
+    @Getter @Setter
+    private Timestamp startedAt;
+
+    @Column(nullable=true)
+    @Getter @Setter
+    private Timestamp completedAt;
+
+    @Column(nullable=true, length = 2000, name="result")
+    @Getter @Setter
+    private Bookmark result;
+
+    @Lob @Basic(fetch=FetchType.LAZY)
+    @Column(nullable=true, columnDefinition="CLOB")
+    @Getter @Setter
+    private String exception;
+
+}
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
index 7ac0c61792..adf2307efc 100644
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
@@ -45,8 +45,8 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.InteractionProvider;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 import org.apache.isis.extensions.commandlog.applib.command.ReplayState;
 import org.apache.isis.extensions.commandlog.jpa.IsisModuleExtCommandLogJpa;
 import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -69,7 +69,7 @@ import lombok.val;
 @RequiredArgsConstructor
 //@Log4j2
 public class CommandJpaRepository
-implements CommandLogRepository<CommandJpa> {
+implements ICommandLogRepository<CommandJpa> {
 
     @Inject final Provider<InteractionProvider> interactionProviderProvider;
     @Inject final Provider<RepositoryService> repositoryServiceProvider;
@@ -322,4 +322,5 @@ implements CommandLogRepository<CommandJpa> {
         return repositoryServiceProvider.get();
     }
 
+
 }
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalOnPrimaryService.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalOnPrimaryService.java
index dc96f4402d..5bf60b9a41 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalOnPrimaryService.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalOnPrimaryService.java
@@ -38,8 +38,8 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository.NotFoundException;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository.NotFoundException;
 import org.apache.isis.extensions.commandreplay.primary.IsisModuleExtCommandReplayPrimary;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
@@ -59,7 +59,7 @@ public class CommandRetrievalOnPrimaryService {
 
     public static class FindCommandsOnPrimaryFromDomainEvent extends ActionDomainEvent { }
 
-    @Inject CommandLogRepository<? extends CommandLog> commandLogRepository;
+    @Inject ICommandLogRepository<? extends CommandLog> commandLogRepository;
 
     /**
      * TODO: outdated info ...
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
index 8edb25290e..04d4b8a247 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
@@ -42,8 +42,8 @@ import org.apache.isis.applib.services.message.MessageService;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository.NotFoundException;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository.NotFoundException;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
 import org.apache.isis.extensions.commandreplay.primary.IsisModuleExtCommandReplayPrimary;
 import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -68,7 +68,7 @@ import lombok.RequiredArgsConstructor;
 //@Log4j2
 public class CommandReplayOnPrimaryService {
 
-    @Inject final CommandLogRepository<? extends CommandLog> commandLogRepository;
+    @Inject final ICommandLogRepository<? extends CommandLog> commandLogRepository;
     @Inject final JaxbService jaxbService;
     @Inject final MessageService messageService;
     @Inject final ContentMappingServiceForCommandsDto contentMappingServiceForCommandsDto;
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
index b92df7d84f..654a8ad770 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
@@ -29,7 +29,7 @@ import javax.inject.Inject;
 import org.apache.isis.applib.services.command.CommandExecutorService;
 import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 import org.apache.isis.extensions.commandlog.applib.command.ReplayState;
 import org.apache.isis.extensions.commandreplay.secondary.analysis.CommandReplayAnalysisService;
 import org.apache.isis.extensions.commandreplay.secondary.fetch.CommandFetcher;
@@ -58,7 +58,7 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
     @Inject CommandExecutorService commandExecutorService;
     @Inject TransactionService transactionService;
     @Inject CommandFetcher commandFetcher;
-    @Inject CommandLogRepository<CommandLog> commandLogRepository;
+    @Inject ICommandLogRepository<CommandLog> commandLogRepository;
     @Inject CommandReplayAnalysisService analysisService;
     @Inject Optional<ReplayCommandExecutionController> controller;
 
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandLog_replayQueue.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandLog_replayQueue.java
index 681829db10..001f7bfcdb 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandLog_replayQueue.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandLog_replayQueue.java
@@ -25,7 +25,7 @@ import javax.inject.Inject;
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.extensions.commandlog.applib.command.CommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 import org.apache.isis.extensions.commandreplay.secondary.IsisModuleExtCommandReplaySecondary;
 import org.apache.isis.extensions.commandreplay.secondary.config.SecondaryConfig;
 
@@ -57,6 +57,6 @@ public class CommandLog_replayQueue {
     }
 
     @Inject SecondaryConfig secondaryConfig;
-    @Inject CommandLogRepository<CommandLog> commandLogRepository;
+    @Inject ICommandLogRepository<CommandLog> commandLogRepository;
 
 }
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
index 8240a539ee..1a1ec34a26 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
@@ -35,7 +35,7 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.extensions.commandlog.applib.command.ICommandLog;
-import org.apache.isis.extensions.commandlog.applib.command.CommandLogRepository;
+import org.apache.isis.extensions.commandlog.applib.command.ICommandLogRepository;
 import org.apache.isis.extensions.commandreplay.secondary.IsisModuleExtCommandReplaySecondary;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
@@ -59,7 +59,7 @@ import lombok.val;
 //@Log4j2
 public class CommandReplayOnSecondaryService {
 
-    @Inject CommandLogRepository<? extends ICommandLog> commandLogRepository;
+    @Inject ICommandLogRepository<? extends ICommandLog> commandLogRepository;
     @Inject JaxbService jaxbService;
 
     public static abstract class ActionDomainEvent<T> extends IsisModuleExtCommandReplaySecondary.ActionDomainEvent<T> { }
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 40c1a3bae9..867a611b10 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -313,11 +313,6 @@
 								<artifactId>datanucleus-api-jdo</artifactId>
 								<version>${datanucleus-api-jdo.version}</version>
 							</dependency>
-							<dependency>
-								<groupId>org.datanucleus</groupId>
-								<artifactId>datanucleus-api-jpa</artifactId>
-								<version>${datanucleus-api-jpa.version}</version>
-							</dependency>
 							<dependency>
 								<groupId>org.datanucleus</groupId>
 								<artifactId>datanucleus-jodatime</artifactId>