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 2021/05/23 08:31:24 UTC

[isis] branch master updated: ISIS-2513: Demo: implement Blob, Clob, Image et al. for JPA

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 4d58aa8  ISIS-2513: Demo: implement Blob, Clob, Image et al. for JPA
4d58aa8 is described below

commit 4d58aa851beece4e7ea66d3915fbf2f0172dd920
Author: andi-huber <ah...@apache.org>
AuthorDate: Sun May 23 10:31:09 2021 +0200

    ISIS-2513: Demo: implement Blob, Clob, Image et al. for JPA
---
 .../src/main/java/demoapp/dom/DemoModuleJpa.java   |  18 +++
 .../demoapp/dom/types/isis/blobs/IsisBlobs.java    |  12 +-
 .../isis/blobs/jpa/IsisBlobJpa-description.adoc    |  24 ++++
 .../dom/types/isis/blobs/jpa/IsisBlobJpa.java      | 118 ++++++++++++++++++++
 .../types/isis/blobs/jpa/IsisBlobJpaEntities.java  |  42 +++++++
 .../demoapp/dom/types/isis/clobs/IsisClobs.java    |  12 +-
 .../isis/clobs/jpa/IsisClobJpa-description.adoc    |  25 +++++
 .../dom/types/isis/clobs/jpa/IsisClobJpa.java      | 124 +++++++++++++++++++++
 .../types/isis/clobs/jpa/IsisClobJpaEntities.java  |  42 +++++++
 .../localresourcepaths/IsisLocalResourcePaths.java |  12 +-
 .../jpa/IsisLocalResourcePathJpa-description.adoc  |  26 +++++
 .../jpa/IsisLocalResourcePathJpa.java              |  97 ++++++++++++++++
 .../jpa/IsisLocalResourcePathJpaEntities.java      |  42 +++++++
 .../dom/types/isis/markups/IsisMarkups.java        |  12 +-
 .../markups/jpa/IsisMarkupJpa-description.adoc     |  26 +++++
 .../dom/types/isis/markups/jpa/IsisMarkupJpa.java  | 103 +++++++++++++++++
 .../isis/markups/jpa/IsisMarkupJpaEntities.java    |  42 +++++++
 .../dom/types/isis/passwords/IsisPasswords.java    |  12 +-
 .../passwords/jpa/IsisPasswordJpa-description.adoc |  23 ++++
 .../types/isis/passwords/jpa/IsisPasswordJpa.java  |  95 ++++++++++++++++
 .../passwords/jpa/IsisPasswordJpaEntities.java     |  42 +++++++
 .../dom/types/isisext/asciidocs/IsisAsciiDocs.java |  12 +-
 .../asciidocs/jpa/IsisAsciiDocJpa-description.adoc |  23 ++++
 .../isisext/asciidocs/jpa/IsisAsciiDocJpa.java     | 103 +++++++++++++++++
 .../asciidocs/jpa/IsisAsciiDocJpaEntities.java     |  42 +++++++
 .../dom/types/isisext/markdowns/IsisMarkdowns.java |  12 +-
 .../markdowns/jpa/IsisMarkdownJpa-description.adoc |  23 ++++
 .../isisext/markdowns/jpa/IsisMarkdownJpa.java     | 103 +++++++++++++++++
 .../markdowns/jpa/IsisMarkdownJpaEntities.java     |  42 +++++++
 .../javaawt/images/JavaAwtBufferedImages.java      |  12 +-
 .../types/javaawt/images/jdo/JavaAwtImageJdo.java  |   2 +-
 .../images/jpa/JavaAwtImageJpa-description.adoc    |  24 ++++
 .../JavaAwtImageJpa.java}                          |  37 ++++--
 .../images/jpa/JavaAwtImageJpaEntities.java        |  42 +++++++
 34 files changed, 1358 insertions(+), 68 deletions(-)

diff --git a/examples/demo/domain/src/main/java/demoapp/dom/DemoModuleJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/DemoModuleJpa.java
index 162d354..323561f 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/DemoModuleJpa.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/DemoModuleJpa.java
@@ -26,6 +26,14 @@ import org.springframework.context.annotation.Profile;
 import org.apache.isis.extensions.commandlog.jpa.IsisModuleExtCommandLogJpa;
 import org.apache.isis.persistence.jpa.eclipselink.IsisModuleJpaEclipselink;
 
+import demoapp.dom.types.isis.blobs.jpa.IsisBlobJpa;
+import demoapp.dom.types.isis.clobs.jpa.IsisClobJpa;
+import demoapp.dom.types.isis.localresourcepaths.jpa.IsisLocalResourcePathJpa;
+import demoapp.dom.types.isis.markups.jpa.IsisMarkupJpa;
+import demoapp.dom.types.isis.passwords.jpa.IsisPasswordJpa;
+import demoapp.dom.types.isisext.asciidocs.jpa.IsisAsciiDocJpa;
+import demoapp.dom.types.isisext.markdowns.jpa.IsisMarkdownJpa;
+import demoapp.dom.types.javaawt.images.jpa.JavaAwtImageJpa;
 import demoapp.dom.types.javalang.booleans.jpa.WrapperBooleanJpa;
 import demoapp.dom.types.javalang.bytes.jpa.WrapperByteJpa;
 import demoapp.dom.types.javalang.characters.jpa.WrapperCharacterJpa;
@@ -65,6 +73,16 @@ import demoapp.dom.types.primitive.shorts.jpa.PrimitiveShortJpa;
 })
 @EntityScan(basePackageClasses = {
 
+        IsisBlobJpa.class,
+        IsisClobJpa.class,
+        IsisLocalResourcePathJpa.class,
+        IsisMarkupJpa.class,
+        IsisPasswordJpa.class,
+        IsisAsciiDocJpa.class,
+        IsisMarkdownJpa.class,
+
+        JavaAwtImageJpa.class,
+
         JavaLangStringJpa.class,
 
         JavaMathBigDecimalJpa.class,
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/IsisBlobs.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/IsisBlobs.java
index da78dc1..e44912b 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/IsisBlobs.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/IsisBlobs.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.value.Blob;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isis.blobs.jdo.IsisBlobJdo;
-import demoapp.dom.types.isis.blobs.jdo.IsisBlobJdoEntities;
+import demoapp.dom.types.isis.blobs.persistence.IsisBlobEntity;
 import demoapp.dom.types.isis.blobs.vm.IsisBlobVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisBlobs", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisBlobs implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisBlobs implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisBlobJdo> getEntities() {
+    public List<? extends IsisBlobEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisBlobJdoEntities entities;
+    ValueHolderRepository<Blob, ? extends IsisBlobEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpa-description.adoc
new file mode 100644
index 0000000..ff6e3af
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpa-description.adoc
@@ -0,0 +1,24 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+Apache Isis implements the relevant JDO extension points to allow the `Blob` type to be persisted.
+To do so correctly, it requires that the column names for the three constituent parts of a `Blob` are specified using `@Column`:
+
+[source,java]
+----
+include::IsisBlobJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisBlobs-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpa.java
new file mode 100644
index 0000000..fbd632b
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpa.java
@@ -0,0 +1,118 @@
+/*
+ *  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 demoapp.dom.types.isis.blobs.jpa;
+
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.Persistent;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.value.Blob;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+
+import demoapp.dom.types.isis.blobs.persistence.IsisBlobEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisBlobJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisBlobEntity"
+)
+@NoArgsConstructor
+public class IsisBlobJpa
+        extends IsisBlobEntity {
+
+//end::class[]
+    public IsisBlobJpa(Blob initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Title(prepend = "Blob JPA entity: ")
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Persistent(defaultFetchGroup="false", columns = {              // <.>
+            @Column(name = "readOnlyProperty_name"),
+            @Column(name = "readOnlyProperty_mimetype"),
+            @Column(name = "readOnlyProperty_bytes")
+    })
+    @Getter @Setter
+    private Blob readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                            // <.>
+    @PropertyLayout(fieldSetId = "editable-properties", sequence = "1")
+    @Persistent(defaultFetchGroup="false", columns = {
+            @Column(name = "readWriteProperty_name"),
+            @Column(name = "readWriteProperty_mimetype"),
+            @Column(name = "readWriteProperty_bytes")
+    })
+    @Getter @Setter
+    private Blob readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                   // <.>
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "1")
+    @Persistent(defaultFetchGroup="false", columns = {
+            @Column(name = "readOnlyOptionalProperty_name",
+                    allowsNull = "true"),                           // <.>
+            @Column(name = "readOnlyOptionalProperty_mimetype",
+                    allowsNull = "true"),
+            @Column(name = "readOnlyOptionalProperty_bytes",
+                    allowsNull = "true")
+    })
+    @Getter @Setter
+    private Blob readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "2")
+    @Persistent(defaultFetchGroup="false", columns = {
+            @Column(name = "readWriteOptionalProperty_name",
+                    allowsNull = "true"),
+            @Column(name = "readWriteOptionalProperty_mimetype",
+                    allowsNull = "true"),
+            @Column(name = "readWriteOptionalProperty_bytes",
+                    allowsNull = "true")
+    })
+    @Getter @Setter
+    private Blob readWriteOptionalProperty;
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpaEntities.java
new file mode 100644
index 0000000..07992a1
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/blobs/jpa/IsisBlobJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isis.blobs.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.value.Blob;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisBlobJpaEntities
+extends ValueHolderRepository<Blob, IsisBlobJpa> {
+
+    protected IsisBlobJpaEntities() {
+        super(IsisBlobJpa.class);
+    }
+
+    @Override
+    protected IsisBlobJpa newDetachedEntity(Blob value) {
+        return new IsisBlobJpa(value);
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/IsisClobs.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/IsisClobs.java
index 66bcb83..d3bc9f8 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/IsisClobs.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/IsisClobs.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.value.Clob;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isis.clobs.jdo.IsisClobJdo;
-import demoapp.dom.types.isis.clobs.jdo.IsisClobJdoEntities;
+import demoapp.dom.types.isis.clobs.persistence.IsisClobEntity;
 import demoapp.dom.types.isis.clobs.vm.IsisClobVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisClobs", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisClobs implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisClobs implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisClobJdo> getEntities() {
+    public List<? extends IsisClobEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisClobJdoEntities entities;
+    ValueHolderRepository<Clob, ? extends IsisClobEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpa-description.adoc
new file mode 100644
index 0000000..c54473d
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpa-description.adoc
@@ -0,0 +1,25 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+Apache Isis implements the relevant JDO extension points to allow the `Clob` type to be persisted.
+To do so correctly, it requires that the column names for the three constituent parts of a `Clob` are specified using `@Column`.
+It's also necessary to specify the `jdbcType` to hold the characters:
+
+[source,java]
+----
+include::IsisClobJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisClobs-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpa.java
new file mode 100644
index 0000000..3784fd4
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpa.java
@@ -0,0 +1,124 @@
+/*
+ *  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 demoapp.dom.types.isis.clobs.jpa;
+
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.Persistent;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.value.Clob;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+
+import demoapp.dom.types.isis.clobs.persistence.IsisClobEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisClobJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisClobEntity"
+)
+@NoArgsConstructor
+public class IsisClobJpa
+        extends IsisClobEntity {
+
+//end::class[]
+    public IsisClobJpa(Clob initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Title(prepend = "Clob JPA entity: ")
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Persistent(defaultFetchGroup="false", columns = {              // <.>
+            @Column(name = "readOnlyProperty_name"),
+            @Column(name = "readOnlyProperty_mimetype"),
+            @Column(name = "readOnlyProperty_chars"
+                    , jdbcType = "CLOB"
+            )
+    })
+    @Getter @Setter
+    private Clob readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                            // <.>
+    @PropertyLayout(fieldSetId = "editable-properties", sequence = "1")
+    @Persistent(defaultFetchGroup="false", columns = {
+            @Column(name = "readWriteProperty_name"),
+            @Column(name = "readWriteProperty_mimetype"),
+            @Column(name = "readWriteProperty_chars"
+                    , jdbcType = "CLOB"
+            )
+    })
+    @Getter @Setter
+    private Clob readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                   // <.>
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "1")
+    @Persistent(defaultFetchGroup="false", columns = {
+            @Column(name = "readOnlyOptionalProperty_name",
+                    allowsNull = "true"),                           // <.>
+            @Column(name = "readOnlyOptionalProperty_mimetype",
+                    allowsNull = "true"),
+            @Column(name = "readOnlyOptionalProperty_chars"
+                    , jdbcType = "CLOB"
+                    , allowsNull = "true")
+    })
+    @Getter @Setter
+    private Clob readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "2")
+    @Persistent(defaultFetchGroup="false", columns = {
+            @Column(name = "readWriteOptionalProperty_name"
+                    , allowsNull = "true"),                           // <.>
+            @Column(name = "readWriteOptionalProperty_mimetype"
+                    , allowsNull = "true"),
+            @Column(name = "readWriteOptionalProperty_bytes"
+                    , jdbcType = "CLOB"
+                    , allowsNull = "true")
+    })
+    @Getter @Setter
+    private Clob readWriteOptionalProperty;
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpaEntities.java
new file mode 100644
index 0000000..0190330
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/clobs/jpa/IsisClobJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isis.clobs.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.value.Clob;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisClobJpaEntities
+extends ValueHolderRepository<Clob, IsisClobJpa> {
+
+    protected IsisClobJpaEntities() {
+        super(IsisClobJpa.class);
+    }
+
+    @Override
+    protected IsisClobJpa newDetachedEntity(Clob value) {
+        return new IsisClobJpa(value);
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/IsisLocalResourcePaths.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/IsisLocalResourcePaths.java
index 9345a08..2305461 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/IsisLocalResourcePaths.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/IsisLocalResourcePaths.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.value.LocalResourcePath;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isis.localresourcepaths.jdo.IsisLocalResourcePathJdo;
-import demoapp.dom.types.isis.localresourcepaths.jdo.IsisLocalResourcePathJdoEntities;
+import demoapp.dom.types.isis.localresourcepaths.persistence.IsisLocalResourcePathEntity;
 import demoapp.dom.types.isis.localresourcepaths.vm.IsisLocalResourcePathVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisLocalResourcePaths", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisLocalResourcePaths implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisLocalResourcePaths implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisLocalResourcePathJdo> getEntities() {
+    public List<? extends IsisLocalResourcePathEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisLocalResourcePathJdoEntities entities;
+    ValueHolderRepository<LocalResourcePath, ? extends IsisLocalResourcePathEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpa-description.adoc
new file mode 100644
index 0000000..86f34c9
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpa-description.adoc
@@ -0,0 +1,26 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+Apache Isis implements the relevant JDO extension points to allow the `LocalResourcePath` type to be persisted.
+No special annotations are required, though `@Column` can be used to fine-tune the mapping.
+The most common usage is to indicate whether the column can be null.
+Since the column ultimately maps down to a string, the column's length could also be specified:
+
+[source,java]
+----
+include::IsisLocalResourcePathJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisLocalResourcePaths-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpa.java
new file mode 100644
index 0000000..a51a93e
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpa.java
@@ -0,0 +1,97 @@
+/*
+ *  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 demoapp.dom.types.isis.localresourcepaths.jpa;
+
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.NotPersistent;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.value.LocalResourcePath;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+
+import demoapp.dom.types.isis.localresourcepaths.persistence.IsisLocalResourcePathEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisLocalResourcePathJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisLocalResourcePathEntity"
+)
+@NoArgsConstructor
+public class IsisLocalResourcePathJpa
+        extends IsisLocalResourcePathEntity {
+
+//end::class[]
+    public IsisLocalResourcePathJpa(LocalResourcePath initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Title(prepend = "LocalResourcePath JPA entity: ")
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Column(allowsNull = "false")                                       // <.>
+    @Getter @Setter
+    private LocalResourcePath readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                                // <.>
+    @PropertyLayout(fieldSetId = "editable-properties", sequence = "1")
+    @Column(allowsNull = "false")
+    @Getter @Setter
+    private LocalResourcePath readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                       // <.>
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "1")
+    @Column(allowsNull = "true")                                        // <.>
+    @Getter @Setter
+    private LocalResourcePath readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "2")
+    @NotPersistent
+    // @Column(allowsNull = "true")
+    @Getter @Setter
+    private LocalResourcePath readWriteOptionalProperty;
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpaEntities.java
new file mode 100644
index 0000000..db2ba04
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/localresourcepaths/jpa/IsisLocalResourcePathJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isis.localresourcepaths.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.value.LocalResourcePath;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisLocalResourcePathJpaEntities
+extends ValueHolderRepository<LocalResourcePath, IsisLocalResourcePathJpa> {
+
+    protected IsisLocalResourcePathJpaEntities() {
+        super(IsisLocalResourcePathJpa.class);
+    }
+
+    @Override
+    protected IsisLocalResourcePathJpa newDetachedEntity(LocalResourcePath value) {
+        return new IsisLocalResourcePathJpa(value);
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/IsisMarkups.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/IsisMarkups.java
index 7ac0a33..48e52a0 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/IsisMarkups.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/IsisMarkups.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.value.Markup;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isis.markups.jdo.IsisMarkupJdo;
-import demoapp.dom.types.isis.markups.jdo.IsisMarkupJdoEntities;
+import demoapp.dom.types.isis.markups.persistence.IsisMarkupEntity;
 import demoapp.dom.types.isis.markups.vm.IsisMarkupVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisMarkups", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisMarkups implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisMarkups implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisMarkupJdo> getEntities() {
+    public List<? extends IsisMarkupEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisMarkupJdoEntities entities;
+    ValueHolderRepository<Markup, ? extends IsisMarkupEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpa-description.adoc
new file mode 100644
index 0000000..069d527
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpa-description.adoc
@@ -0,0 +1,26 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+Apache Isis implements the relevant JDO extension points to allow the `Markup` type to be persisted.
+No special annotations are required, though `@Column` can be used to fine-tune the mapping.
+The most common usage is to indicate whether the column can be null.
+Since the column ultimately maps down to a long string, the `jdbcType` should also be specified:
+
+[source,java]
+----
+include::IsisMarkupJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisMarkups-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpa.java
new file mode 100644
index 0000000..cbb6073
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpa.java
@@ -0,0 +1,103 @@
+/*
+ *  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 demoapp.dom.types.isis.markups.jpa;
+
+import javax.inject.Inject;
+import javax.jdo.annotations.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.value.Markup;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+
+import demoapp.dom.types.isis.markups.persistence.IsisMarkupEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisMarkupJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisMarkupEntity"
+)
+@NoArgsConstructor
+public class IsisMarkupJpa
+        extends IsisMarkupEntity {
+
+//end::class[]
+    public IsisMarkupJpa(Markup initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    public String title() {
+        return "Markup JPA entity: " +
+            bookmarkService.bookmarkForElseFail(this).getIdentifier();
+    }
+
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Column(allowsNull = "false", jdbcType = "CLOB")                // <.>
+    @Getter @Setter
+    private Markup readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                            // <.>
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "editable-properties", sequence = "1")
+    @Column(allowsNull = "false", jdbcType = "CLOB")
+    @Getter @Setter
+    private Markup readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                   // <.>
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "optional-properties", sequence = "1")
+    @Column(allowsNull = "true")                                    // <.>
+    @Getter @Setter
+    private Markup readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "optional-properties", sequence = "2")
+    @Column(allowsNull = "true")
+    @Getter @Setter
+    private Markup readWriteOptionalProperty;
+
+    @Inject
+    private BookmarkService bookmarkService;
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpaEntities.java
new file mode 100644
index 0000000..7ac8492
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/markups/jpa/IsisMarkupJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isis.markups.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.value.Markup;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisMarkupJpaEntities
+extends ValueHolderRepository<Markup, IsisMarkupJpa> {
+
+    protected IsisMarkupJpaEntities() {
+        super(IsisMarkupJpa.class);
+    }
+
+    @Override
+    protected IsisMarkupJpa newDetachedEntity(Markup value) {
+        return new IsisMarkupJpa(value);
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/IsisPasswords.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/IsisPasswords.java
index fd433eb..9172ef4 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/IsisPasswords.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/IsisPasswords.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.value.Password;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isis.passwords.jdo.IsisPasswordJdo;
-import demoapp.dom.types.isis.passwords.jdo.IsisPasswordJdoEntities;
+import demoapp.dom.types.isis.passwords.persistence.IsisPasswordEntity;
 import demoapp.dom.types.isis.passwords.vm.IsisPasswordVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisPasswords", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisPasswords implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisPasswords implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisPasswordJdo> getEntities() {
+    public List<? extends IsisPasswordEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisPasswordJdoEntities entities;
+    ValueHolderRepository<Password, ? extends IsisPasswordEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpa-description.adoc
new file mode 100644
index 0000000..3731726
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpa-description.adoc
@@ -0,0 +1,23 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+JDO supports `Password` out-of-the-box, so no special annotations are required.
+
+[source,java]
+----
+include::IsisPasswordJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisPasswords-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpa.java
new file mode 100644
index 0000000..6af3acc
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpa.java
@@ -0,0 +1,95 @@
+/*
+ *  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 demoapp.dom.types.isis.passwords.jpa;
+
+import javax.jdo.annotations.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.value.Password;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+
+import demoapp.dom.types.isis.passwords.persistence.IsisPasswordEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisPasswordJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisPasswordEntity"
+)
+@NoArgsConstructor
+public class IsisPasswordJpa
+        extends IsisPasswordEntity {
+
+//end::class[]
+    public IsisPasswordJpa(Password initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    @Title(prepend = "Password JPA entity: ")
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Column(allowsNull = "false")                                               // <.>
+    @Getter @Setter
+    private Password readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                                        // <.>
+    @PropertyLayout(fieldSetId = "editable-properties", sequence = "1")
+    @Column(allowsNull = "false")
+    @Getter @Setter
+    private Password readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                               // <.>
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "1")
+    @Column(allowsNull = "true")                                                // <.>
+    @Getter @Setter
+    private Password readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(fieldSetId = "optional-properties", sequence = "2")
+    @Column(allowsNull = "true")
+    @Getter @Setter
+    private Password readWriteOptionalProperty;
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpaEntities.java
new file mode 100644
index 0000000..8bc95e5
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isis/passwords/jpa/IsisPasswordJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isis.passwords.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.value.Password;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisPasswordJpaEntities
+extends ValueHolderRepository<Password, IsisPasswordJpa> {
+
+    protected IsisPasswordJpaEntities() {
+        super(IsisPasswordJpa.class);
+    }
+
+    @Override
+    protected IsisPasswordJpa newDetachedEntity(Password value) {
+        return new IsisPasswordJpa(value);
+    }
+
+}
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/IsisAsciiDocs.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/IsisAsciiDocs.java
index 03c1000..e1463ad 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/IsisAsciiDocs.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/IsisAsciiDocs.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.valuetypes.asciidoc.applib.value.AsciiDoc;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isisext.asciidocs.jdo.IsisAsciiDocJdo;
-import demoapp.dom.types.isisext.asciidocs.jdo.IsisAsciiDocJdoEntities;
+import demoapp.dom.types.isisext.asciidocs.persistence.IsisAsciiDocEntity;
 import demoapp.dom.types.isisext.asciidocs.vm.IsisAsciiDocVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisAsciiDocs", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisAsciiDocs implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisAsciiDocs implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisAsciiDocJdo> getEntities() {
+    public List<? extends IsisAsciiDocEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisAsciiDocJdoEntities entities;
+    ValueHolderRepository<AsciiDoc, ? extends IsisAsciiDocEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpa-description.adoc
new file mode 100644
index 0000000..a417189
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpa-description.adoc
@@ -0,0 +1,23 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+JDO supports `AsciiDoc` out-of-the-box, so no special annotations are required.
+
+[source,java]
+----
+include::IsisAsciiDocJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisAsciiDocs-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpa.java
new file mode 100644
index 0000000..7a8b453
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpa.java
@@ -0,0 +1,103 @@
+/*
+ *  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 demoapp.dom.types.isisext.asciidocs.jpa;
+
+import javax.inject.Inject;
+import javax.jdo.annotations.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+import org.apache.isis.valuetypes.asciidoc.applib.value.AsciiDoc;
+
+import demoapp.dom.types.isisext.asciidocs.persistence.IsisAsciiDocEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisAsciiDocJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisAsciiDocEntity"
+)
+@NoArgsConstructor
+public class IsisAsciiDocJpa
+        extends IsisAsciiDocEntity {
+
+//end::class[]
+    public IsisAsciiDocJpa(AsciiDoc initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    public String title() {
+        return "AsciiDoc JPA entity: " +
+            bookmarkService.bookmarkForElseFail(this).getIdentifier();
+    }
+
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Column(allowsNull = "false", jdbcType = "CLOB")                // <.>
+    @Getter @Setter
+    private AsciiDoc readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                            // <.>
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "editable-properties", sequence = "1")
+    @Column(allowsNull = "false", jdbcType = "CLOB")
+    @Getter @Setter
+    private AsciiDoc readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                   // <.>
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "optional-properties", sequence = "1")
+    @Column(allowsNull = "true")                                    // <.>
+    @Getter @Setter
+    private AsciiDoc readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "optional-properties", sequence = "2")
+    @Column(allowsNull = "true")
+    @Getter @Setter
+    private AsciiDoc readWriteOptionalProperty;
+
+    @Inject
+    private BookmarkService bookmarkService;
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpaEntities.java
new file mode 100644
index 0000000..460a391
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/asciidocs/jpa/IsisAsciiDocJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isisext.asciidocs.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.valuetypes.asciidoc.applib.value.AsciiDoc;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisAsciiDocJpaEntities
+extends ValueHolderRepository<AsciiDoc, IsisAsciiDocJpa> {
+
+    protected IsisAsciiDocJpaEntities() {
+        super(IsisAsciiDocJpa.class);
+    }
+
+    @Override
+    protected IsisAsciiDocJpa newDetachedEntity(AsciiDoc value) {
+        return new IsisAsciiDocJpa(value);
+    }
+
+}
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/IsisMarkdowns.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/IsisMarkdowns.java
index 602d38d..8aac944 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/IsisMarkdowns.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/IsisMarkdowns.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.valuetypes.markdown.applib.value.Markdown;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.isisext.markdowns.jdo.IsisMarkdownJdo;
-import demoapp.dom.types.isisext.markdowns.jdo.IsisMarkdownJdoEntities;
+import demoapp.dom.types.isisext.markdowns.persistence.IsisMarkdownEntity;
 import demoapp.dom.types.isisext.markdowns.vm.IsisMarkdownVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.IsisMarkdowns", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class IsisMarkdowns implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class IsisMarkdowns implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<IsisMarkdownJdo> getEntities() {
+    public List<? extends IsisMarkdownEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    IsisMarkdownJdoEntities entities;
+    ValueHolderRepository<Markdown, ? extends IsisMarkdownEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpa-description.adoc
new file mode 100644
index 0000000..e2035ec
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpa-description.adoc
@@ -0,0 +1,23 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+JDO supports `Markdown` out-of-the-box, so no special annotations are required.
+
+[source,java]
+----
+include::IsisMarkdownJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../IsisMarkdowns-common.adoc[]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpa.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpa.java
new file mode 100644
index 0000000..ad3e04f
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpa.java
@@ -0,0 +1,103 @@
+/*
+ *  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 demoapp.dom.types.isisext.markdowns.jpa;
+
+import javax.inject.Inject;
+import javax.jdo.annotations.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
+import org.apache.isis.valuetypes.markdown.applib.value.Markdown;
+
+import demoapp.dom.types.isisext.markdowns.persistence.IsisMarkdownEntity;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Profile("demo-jpa")
+//tag::class[]
+@Entity
+@Table(
+      schema = "demo",
+      name = "IsisMarkdownJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
+@DomainObject(
+      objectType = "demo.IsisMarkdownEntity"
+)
+@NoArgsConstructor
+public class IsisMarkdownJpa
+        extends IsisMarkdownEntity {
+
+//end::class[]
+    public IsisMarkdownJpa(Markdown initialValue) {
+        this.readOnlyProperty = initialValue;
+        this.readWriteProperty = initialValue;
+    }
+
+//tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
+    public String title() {
+        return "Markdown JPA entity: " +
+            bookmarkService.bookmarkForElseFail(this).getIdentifier();
+    }
+
+    @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
+    @Column(allowsNull = "false", jdbcType = "CLOB")                // <.>
+    @Getter @Setter
+    private Markdown readOnlyProperty;
+
+    @Property(editing = Editing.ENABLED)                            // <.>
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "editable-properties", sequence = "1")
+    @Column(allowsNull = "false", jdbcType = "CLOB")
+    @Getter @Setter
+    private Markdown readWriteProperty;
+
+    @Property(optionality = Optionality.OPTIONAL)                   // <.>
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "optional-properties", sequence = "1")
+    @Column(allowsNull = "true")                                    // <.>
+    @Getter @Setter
+    private Markdown readOnlyOptionalProperty;
+
+    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
+    @PropertyLayout(hidden = Where.ALL_TABLES, fieldSetId = "optional-properties", sequence = "2")
+    @Column(allowsNull = "true")
+    @Getter @Setter
+    private Markdown readWriteOptionalProperty;
+
+    @Inject
+    private BookmarkService bookmarkService;
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpaEntities.java
new file mode 100644
index 0000000..878b693
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/isisext/markdowns/jpa/IsisMarkdownJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.isisext.markdowns.jpa;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.valuetypes.markdown.applib.value.Markdown;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class IsisMarkdownJpaEntities
+extends ValueHolderRepository<Markdown, IsisMarkdownJpa> {
+
+    protected IsisMarkdownJpaEntities() {
+        super(IsisMarkdownJpa.class);
+    }
+
+    @Override
+    protected IsisMarkdownJpa newDetachedEntity(Markdown value) {
+        return new IsisMarkdownJpa(value);
+    }
+
+}
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/JavaAwtBufferedImages.java b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/JavaAwtBufferedImages.java
index 744559f..c47a5c9 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/JavaAwtBufferedImages.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/JavaAwtBufferedImages.java
@@ -37,19 +37,17 @@ import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.annotation.SemanticsOf;
 
-import lombok.extern.log4j.Log4j2;
-
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.types.Samples;
-import demoapp.dom.types.javaawt.images.jdo.JavaAwtImageJdo;
-import demoapp.dom.types.javaawt.images.jdo.JavaAwtImageJdoEntities;
+import demoapp.dom.types.javaawt.images.persistence.JavaAwtImageEntity;
 import demoapp.dom.types.javaawt.images.vm.JavaAwtImageVm;
 
 @XmlRootElement(name = "Demo")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.JavaAwtBufferedImages", editing=Editing.ENABLED)
-@Log4j2
+//@Log4j2
 public class JavaAwtBufferedImages implements HasAsciiDocDescription {
 
     public String title() {
@@ -66,13 +64,13 @@ public class JavaAwtBufferedImages implements HasAsciiDocDescription {
     }
 
     @Collection
-    public List<JavaAwtImageJdo> getEntities() {
+    public List<? extends JavaAwtImageEntity> getEntities() {
         return entities.all();
     }
 
     @Inject
     @XmlTransient
-    JavaAwtImageJdoEntities entities;
+    ValueHolderRepository<BufferedImage, ? extends JavaAwtImageEntity> entities;
 
     @Inject
     @XmlTransient
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java
index b96ce9a..ffa1295 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java
@@ -41,7 +41,7 @@ import lombok.Setter;
 @DomainObject(
         objectType = "demo.JavaAwtImageJdo"
 )
-public class JavaAwtImageJdo                                          // <.>
+public class JavaAwtImageJdo
         extends JavaAwtImageEntity
 //end::class[]
 // label positions not yet supported
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpa-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpa-description.adoc
new file mode 100644
index 0000000..e7e6f74
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpa-description.adoc
@@ -0,0 +1,24 @@
+:Notice: 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 ag [...]
+
+[WARNING]
+==== 
+TODO this yet is just a copy from JDO
+====
+
+JDO supports `java.awt.image.BufferedImage` out-of-the-box, so no special annotations are required.
+
+[source,java]
+----
+include::JavaAwtImageJpa.java[tags=class]
+----
+<.> a no-arg constructor is introduced by JDO enhancer
+<.> required property as defined to JDO/DataNucleus.
++
+Apache Isis assumes properties are mandatory, so no additional annotation is required.
+// not yet supported
+//<.> directly editable property as defined to Apache Isis
+<.> optional property as defined to Apache Isis
+<.> optional property as defined to JDO/DataNucleus
+
+
+include::../JavaAwtBufferedImages-common.adoc[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpa.java
similarity index 79%
copy from examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java
copy to examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpa.java
index b96ce9a..a18e4db 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jdo/JavaAwtImageJdo.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpa.java
@@ -16,32 +16,43 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.types.javaawt.images.jdo;
+package demoapp.dom.types.javaawt.images.jpa;
 
 import java.awt.image.BufferedImage;
 
 import javax.jdo.annotations.Column;
-import javax.jdo.annotations.DatastoreIdentity;
-import javax.jdo.annotations.IdGeneratorStrategy;
-import javax.jdo.annotations.IdentityType;
-import javax.jdo.annotations.PersistenceCapable;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.springframework.context.annotation.Profile;
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
 
 import demoapp.dom.types.javaawt.images.persistence.JavaAwtImageEntity;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 
+@Profile("demo-jpa")
 //tag::class[]
-@PersistenceCapable(identityType = IdentityType.DATASTORE, schema = "demo")
-@DatastoreIdentity(strategy = IdGeneratorStrategy.IDENTITY, column = "id")
+@Entity
+@Table(
+      schema = "demo",
+      name = "JavaAwtImageJpa"
+)
+@EntityListeners(JpaEntityInjectionPointResolver.class)
 @DomainObject(
-        objectType = "demo.JavaAwtImageJdo"
+      objectType = "demo.JavaAwtImageEntity"
 )
-public class JavaAwtImageJdo                                          // <.>
+@NoArgsConstructor
+public class JavaAwtImageJpa
         extends JavaAwtImageEntity
 //end::class[]
 // label positions not yet supported
@@ -49,18 +60,22 @@ public class JavaAwtImageJdo                                          // <.>
 {
 
 //end::class[]
-    public JavaAwtImageJdo(BufferedImage initialValue) {
+    public JavaAwtImageJpa(BufferedImage initialValue) {
         this.readOnlyProperty = initialValue;
 //        this.readWriteProperty = initialValue;    // editable properties not yet supported
     }
 
     // @Title not yet supported
     public String title() {
-        return "Image JDO entity";
+        return "Image JPA entity";
     }
 
     // @Title(prepend = "Image JDO entity: ")  // not yet supported
 //tag::class[]
+    @Id
+    @GeneratedValue
+    private Long id;
+
     @PropertyLayout(fieldSetId = "read-only-properties", sequence = "1")
     @Column(allowsNull = "false")                                   // <.>
     @Getter @Setter
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpaEntities.java b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpaEntities.java
new file mode 100644
index 0000000..d62999c
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/types/javaawt/images/jpa/JavaAwtImageJpaEntities.java
@@ -0,0 +1,42 @@
+/*
+ *  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 demoapp.dom.types.javaawt.images.jpa;
+
+import java.awt.image.BufferedImage;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import demoapp.dom._infra.values.ValueHolderRepository;
+
+@Profile("demo-jpa")
+@Service
+public class JavaAwtImageJpaEntities
+extends ValueHolderRepository<BufferedImage, JavaAwtImageJpa> {
+
+    protected JavaAwtImageJpaEntities() {
+        super(JavaAwtImageJpa.class);
+    }
+
+    @Override
+    protected JavaAwtImageJpa newDetachedEntity(BufferedImage value) {
+        return new JavaAwtImageJpa(value);
+    }
+
+}