You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2023/09/20 12:55:40 UTC

[causeway] 01/02: CAUSEWAY-3593: makes JPA-specific facet factories mirror JDO

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

danhaywood pushed a commit to branch CAUSEWAY-3593
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit e422a22aacf8b3280b8b496c7d2c91bdd66e6f08
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Sep 20 13:48:06 2023 +0100

    CAUSEWAY-3593: makes JPA-specific facet factories mirror JDO
    
    for BigDecimalFrom{Jdo/Jpa}ColumnAnnotationFacetFactory
    for MandatoryFrom{Jdo/Jpa}ColumnAnnotationFacetFactory
---
 persistence/commons/src/main/java/module-info.java |   1 +
 ...romXxxColumnAnnotationMetaModelRefinerUtil.java |  40 +++++++
 ...romXxxColumnAnnotationMetaModelRefinerUtil.java |  27 +++++
 persistence/jdo/metamodel/pom.xml                  |  31 +++--
 .../jdo/metamodel/src/main/java/module-info.java   |   1 +
 .../jdo/metamodel/JdoProgrammingModel.java         |   8 +-
 ...BigDecimalFromColumnAnnotationFacetFactory.java | 131 ---------------------
 ...DecimalFromJdoColumnAnnotationFacetFactory.java |  88 ++++++++++++++
 ...toryFacetFromAbsenceOfJdoColumnAnnotation.java} |   4 +-
 ... => MandatoryFacetFromJdoColumnAnnotation.java} |   4 +-
 ...datoryFromJdoColumnAnnotationFacetFactory.java} |  81 +++++++------
 ...ctionalDigitsFacetFromJdoColumnAnnotation.java} |  25 +---
 .../MaxLengthFacetFromJdoColumnAnnotation.java     |   2 +-
 ...xLengthFromJdoColumnAnnotationFacetFactory.java |  16 +--
 ...ctionalDigitsFacetFromJdoColumnAnnotation.java} |  30 ++---
 .../metamodel/facets/prop/column/_ColumnUtil.java  |  93 ---------------
 ...malFromJdoColumnAnnotationFacetFactoryTest.java |   6 +-
 ...oryFromJdoColumnAnnotationFacetFactoryTest.java |  20 ++--
 persistence/jpa/metamodel/pom.xml                  |  29 +++--
 .../jpa/metamodel/src/main/java/module-info.java   |   1 +
 .../jpa/metamodel/JpaProgrammingModel.java         |   3 +
 ...DecimalFromJpaColumnAnnotationFacetFactory.java |  17 ++-
 ...ndatoryFromJpaColumnAnnotationFacetFactory.java |  24 +++-
 ...actionalDigitsFacetFromJpaColumnAnnotation.java |  10 +-
 .../MaxLengthFacetFromJpaColumnAnnotation.java}    |  24 ++--
 ...LengthFromJpaColumnAnnotationFacetFactory.java} |  40 +++----
 26 files changed, 350 insertions(+), 406 deletions(-)

diff --git a/persistence/commons/src/main/java/module-info.java b/persistence/commons/src/main/java/module-info.java
index dc127a51db..981355e018 100644
--- a/persistence/commons/src/main/java/module-info.java
+++ b/persistence/commons/src/main/java/module-info.java
@@ -19,6 +19,7 @@
 module org.apache.causeway.persistence.commons {
     exports org.apache.causeway.persistence.jpa.integration.changetracking;
     exports org.apache.causeway.persistence.commons;
+    exports org.apache.causeway.persistence.commons.metamodel.facets.prop.column;
 
     requires java.annotation;
     requires java.desktop;
diff --git a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/metamodel/facets/prop/column/BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil.java b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/metamodel/facets/prop/column/BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil.java
new file mode 100644
index 0000000000..ebb90f58d6
--- /dev/null
+++ b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/metamodel/facets/prop/column/BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil.java
@@ -0,0 +1,40 @@
+package org.apache.causeway.persistence.commons.metamodel.facets.prop.column;
+
+import lombok.experimental.UtilityClass;
+
+import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxFractionalDigitsFacet;
+import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxTotalDigitsFacet;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
+
+@UtilityClass
+public class BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil {
+    public static void validateBigDecimalValueFacet(final ObjectAssociation association) {
+
+        association.lookupFacet(MaxTotalDigitsFacet.class)
+        .map(MaxTotalDigitsFacet::getSharedFacetRankingElseFail)
+        .ifPresent(facetRanking->facetRanking
+                .visitTopRankPairsSemanticDiffering(MaxTotalDigitsFacet.class, (a, b)->{
+
+                    ValidationFailure.raiseFormatted(
+                            association,
+                            "%s: inconsistent MaxTotalDigits semantics specified in %s and %s.",
+                            association.getFeatureIdentifier().toString(),
+                            a.getClass().getSimpleName(),
+                            b.getClass().getSimpleName());
+                }));
+
+        association.lookupFacet(MaxFractionalDigitsFacet.class)
+        .map(MaxFractionalDigitsFacet::getSharedFacetRankingElseFail)
+        .ifPresent(facetRanking->facetRanking
+                .visitTopRankPairsSemanticDiffering(MaxFractionalDigitsFacet.class, (a, b)->{
+
+                    ValidationFailure.raiseFormatted(
+                            association,
+                            "%s: inconsistent MaxFractionalDigits semantics specified in %s and %s.",
+                            association.getFeatureIdentifier().toString(),
+                            a.getClass().getSimpleName(),
+                            b.getClass().getSimpleName());
+                }));
+    }
+}
diff --git a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/metamodel/facets/prop/column/MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil.java b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/metamodel/facets/prop/column/MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil.java
new file mode 100644
index 0000000000..76da577ad8
--- /dev/null
+++ b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/metamodel/facets/prop/column/MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil.java
@@ -0,0 +1,27 @@
+package org.apache.causeway.persistence.commons.metamodel.facets.prop.column;
+
+import lombok.experimental.UtilityClass;
+
+import org.apache.causeway.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
+
+@UtilityClass
+public class MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil {
+    public static void validateMandatoryFacet(final ObjectAssociation association) {
+
+        association.lookupFacet(MandatoryFacet.class)
+        .map(MandatoryFacet::getSharedFacetRankingElseFail)
+        .ifPresent(facetRanking->facetRanking
+                .visitTopRankPairsSemanticDiffering(MandatoryFacet.class, (a, b)->{
+
+                    ValidationFailure.raiseFormatted(
+                            association,
+                            "%s: inconsistent Mandatory/Optional semantics specified in %s and %s.",
+                            association.getFeatureIdentifier().toString(),
+                            a.getClass().getSimpleName(),
+                            b.getClass().getSimpleName());
+                }));
+
+    }
+}
diff --git a/persistence/jdo/metamodel/pom.xml b/persistence/jdo/metamodel/pom.xml
index f3a4586718..bd37c2ac39 100644
--- a/persistence/jdo/metamodel/pom.xml
+++ b/persistence/jdo/metamodel/pom.xml
@@ -1,13 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 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 
+<!-- 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. -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
@@ -37,26 +37,31 @@
 			<groupId>org.apache.causeway.persistence</groupId>
 			<artifactId>causeway-persistence-jdo-applib</artifactId>
 		</dependency>
-		
+
 		<dependency>
 			<groupId>org.apache.causeway.persistence</groupId>
 			<artifactId>causeway-persistence-jdo-provider</artifactId>
 		</dependency>
 
-		<dependency>
+        <dependency>
+            <groupId>org.apache.causeway.persistence</groupId>
+            <artifactId>causeway-persistence-commons</artifactId>
+        </dependency>
+
+        <dependency>
 			<groupId>org.apache.causeway.core</groupId>
 			<artifactId>causeway-core-runtime</artifactId>
 		</dependency>
 
 		<!-- TESTING -->
-		
+
         <dependency>
             <groupId>org.apache.causeway.core</groupId>
             <artifactId>causeway-core-internaltestsupport</artifactId>
             <scope>test</scope>
         </dependency>
 
-	</dependencies>
+    </dependencies>
 
 
 </project>
\ No newline at end of file
diff --git a/persistence/jdo/metamodel/src/main/java/module-info.java b/persistence/jdo/metamodel/src/main/java/module-info.java
index c820846749..f7748fa6d0 100644
--- a/persistence/jdo/metamodel/src/main/java/module-info.java
+++ b/persistence/jdo/metamodel/src/main/java/module-info.java
@@ -28,6 +28,7 @@ module org.apache.causeway.persistence.jdo.metamodel {
     requires javax.jdo;
     requires org.apache.causeway.security.api;
     requires org.datanucleus.store.rdbms;
+    requires org.apache.causeway.persistence.commons;
 
     exports org.apache.causeway.persistence.jdo.metamodel;
     exports org.apache.causeway.persistence.jdo.metamodel.facets.object.persistencecapable;
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/JdoProgrammingModel.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/JdoProgrammingModel.java
index 9ec479f00a..483c62e633 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/JdoProgrammingModel.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/JdoProgrammingModel.java
@@ -36,8 +36,8 @@ import org.apache.causeway.persistence.jdo.metamodel.facets.object.datastoreiden
 import org.apache.causeway.persistence.jdo.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacetFactory;
 import org.apache.causeway.persistence.jdo.metamodel.facets.object.query.JdoQueryAnnotationFacetFactory;
 import org.apache.causeway.persistence.jdo.metamodel.facets.object.version.JdoVersionAnnotationFacetFactory;
-import org.apache.causeway.persistence.jdo.metamodel.facets.prop.column.BigDecimalFromColumnAnnotationFacetFactory;
-import org.apache.causeway.persistence.jdo.metamodel.facets.prop.column.MandatoryFromColumnAnnotationFacetFactory;
+import org.apache.causeway.persistence.jdo.metamodel.facets.prop.column.BigDecimalFromJdoColumnAnnotationFacetFactory;
+import org.apache.causeway.persistence.jdo.metamodel.facets.prop.column.MandatoryFromJdoColumnAnnotationFacetFactory;
 import org.apache.causeway.persistence.jdo.metamodel.facets.prop.column.MaxLengthFromJdoColumnAnnotationFacetFactory;
 import org.apache.causeway.persistence.jdo.metamodel.facets.prop.notpersistent.JdoNotPersistentAnnotationFacetFactory;
 import org.apache.causeway.persistence.jdo.metamodel.facets.prop.primarykey.JdoPrimaryKeyAnnotationFacetFactory;
@@ -82,12 +82,12 @@ public class JdoProgrammingModel implements MetaModelRefiner {
 
         pm.addFactory(step2, new JdoQueryAnnotationFacetFactory(mmc, jdoFacetContext), Marker.JDO);
 
-        pm.addFactory(step2, new BigDecimalFromColumnAnnotationFacetFactory(mmc), Marker.JDO);
+        pm.addFactory(step2, new BigDecimalFromJdoColumnAnnotationFacetFactory(mmc), Marker.JDO);
         pm.addFactory(step2, new MaxLengthFromJdoColumnAnnotationFacetFactory(mmc), Marker.JDO);
         // must appear after JdoPrimaryKeyAnnotationFacetFactory (above)
         // and also MandatoryFacetOnPropertyMandatoryAnnotationFactory
         // and also PropertyAnnotationFactory
-        pm.addFactory(step2, new MandatoryFromColumnAnnotationFacetFactory(mmc, jdoFacetContext), Marker.JDO);
+        pm.addFactory(step2, new MandatoryFromJdoColumnAnnotationFacetFactory(mmc, jdoFacetContext), Marker.JDO);
 
 
         // -- validators
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromColumnAnnotationFacetFactory.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromColumnAnnotationFacetFactory.java
deleted file mode 100644
index 158fcb9693..0000000000
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromColumnAnnotationFacetFactory.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.persistence.jdo.metamodel.facets.prop.column;
-
-import java.math.BigDecimal;
-
-import javax.inject.Inject;
-import javax.jdo.annotations.IdentityType;
-
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
-import org.apache.causeway.core.metamodel.facetapi.FeatureType;
-import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
-import org.apache.causeway.core.metamodel.facets.FacetFactoryAbstract;
-import org.apache.causeway.core.metamodel.facets.FacetedMethod;
-import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxFractionalDigitsFacet;
-import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxTotalDigitsFacet;
-import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
-import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
-import org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
-import org.apache.causeway.persistence.jdo.provider.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
-import org.apache.causeway.persistence.jdo.provider.metamodel.facets.prop.notpersistent.JdoNotPersistentFacet;
-
-public class BigDecimalFromColumnAnnotationFacetFactory
-extends FacetFactoryAbstract
-implements MetaModelRefiner {
-
-    @Inject
-    public BigDecimalFromColumnAnnotationFacetFactory(final MetaModelContext mmc) {
-        super(mmc, FeatureType.PROPERTIES_ONLY);
-    }
-
-    @Override
-    public void process(final ProcessMethodContext processMethodContext) {
-
-        if(BigDecimal.class != processMethodContext.getMethod().getReturnType()) {
-            return;
-        }
-
-        final FacetedMethod holder = processMethodContext.getFacetHolder();
-
-        _ColumnUtil.processColumnAnnotations(processMethodContext,
-                jdoColumnIfAny->{
-                    addFacetIfPresent(
-                            MaxTotalDigitsFacetFromJdoColumnAnnotation
-                            .createJdo(jdoColumnIfAny, holder));
-
-                    addFacetIfPresent(
-                            MaxFractionalDigitsFacetFromJdoColumn
-                            .createJdo(jdoColumnIfAny, holder));
-                },
-                jpaColumnIfAny->{
-                    addFacetIfPresent(
-                            MaxTotalDigitsFacetFromJdoColumnAnnotation
-                            .createJpa(jpaColumnIfAny, holder));
-
-                    addFacetIfPresent(
-                            MaxFractionalDigitsFacetFromJdoColumn
-                            .createJpa(jpaColumnIfAny, holder));
-                });
-
-    }
-
-    @Override
-    public void refineProgrammingModel(final ProgrammingModel programmingModel) {
-        programmingModel.addValidatorSkipManagedBeans(spec->{
-
-            // only consider persistent entities
-            final JdoPersistenceCapableFacet pcFacet = spec.getFacet(JdoPersistenceCapableFacet.class);
-            if(pcFacet==null || pcFacet.getIdentityType() == IdentityType.NONDURABLE) {
-                return;
-            }
-
-            spec.streamProperties(MixedIn.EXCLUDED)
-            // skip checks if annotated with JDO @NotPersistent
-            .filter(association->!association.containsNonFallbackFacet(JdoNotPersistentFacet.class))
-            .forEach(association->{
-                validateBigDecimalValueFacet(association);
-            });
-
-        });
-    }
-
-    private static void validateBigDecimalValueFacet(final ObjectAssociation association) {
-
-        association.lookupFacet(MaxTotalDigitsFacet.class)
-        .map(MaxTotalDigitsFacet::getSharedFacetRankingElseFail)
-        .ifPresent(facetRanking->facetRanking
-                .visitTopRankPairsSemanticDiffering(MaxTotalDigitsFacet.class, (a, b)->{
-
-                    ValidationFailure.raiseFormatted(
-                            association,
-                            "%s: inconsistent MaxTotalDigits semantics specified in %s and %s.",
-                            association.getFeatureIdentifier().toString(),
-                            a.getClass().getSimpleName(),
-                            b.getClass().getSimpleName());
-                }));
-
-        association.lookupFacet(MaxFractionalDigitsFacet.class)
-        .map(MaxFractionalDigitsFacet::getSharedFacetRankingElseFail)
-        .ifPresent(facetRanking->facetRanking
-                .visitTopRankPairsSemanticDiffering(MaxFractionalDigitsFacet.class, (a, b)->{
-
-                    ValidationFailure.raiseFormatted(
-                            association,
-                            "%s: inconsistent MaxFractionalDigits semantics specified in %s and %s.",
-                            association.getFeatureIdentifier().toString(),
-                            a.getClass().getSimpleName(),
-                            b.getClass().getSimpleName());
-                }));
-
-    }
-
-
-}
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactory.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactory.java
new file mode 100644
index 0000000000..10882eddb2
--- /dev/null
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactory.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.persistence.jdo.metamodel.facets.prop.column;
+
+import lombok.val;
+
+import java.math.BigDecimal;
+
+import javax.inject.Inject;
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.IdentityType;
+
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.facetapi.FeatureType;
+import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
+import org.apache.causeway.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.causeway.core.metamodel.facets.FacetedMethod;
+import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.persistence.commons.metamodel.facets.prop.column.BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil;
+import org.apache.causeway.persistence.jdo.provider.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
+import org.apache.causeway.persistence.jdo.provider.metamodel.facets.prop.notpersistent.JdoNotPersistentFacet;
+
+public class BigDecimalFromJdoColumnAnnotationFacetFactory
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
+
+    @Inject
+    public BigDecimalFromJdoColumnAnnotationFacetFactory(final MetaModelContext mmc) {
+        super(mmc, FeatureType.PROPERTIES_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+
+        if(BigDecimal.class != processMethodContext.getMethod().getReturnType()) {
+            return;
+        }
+
+        final FacetedMethod holder = processMethodContext.getFacetHolder();
+
+        val jdoColumnIfAny = processMethodContext.synthesizeOnMethod(Column.class);
+
+        addFacetIfPresent(
+                MaxTotalDigitsFacetFromJdoColumnAnnotation
+                .createJdo(jdoColumnIfAny, holder));
+
+        addFacetIfPresent(
+                MaxFractionalDigitsFacetFromJdoColumnAnnotation
+                .create(jdoColumnIfAny, holder));
+
+    }
+
+    @Override
+    public void refineProgrammingModel(final ProgrammingModel programmingModel) {
+        programmingModel.addValidatorSkipManagedBeans(spec->{
+
+            // only consider persistent entities
+            final JdoPersistenceCapableFacet pcFacet = spec.getFacet(JdoPersistenceCapableFacet.class);
+            if(pcFacet==null || pcFacet.getIdentityType() == IdentityType.NONDURABLE) {
+                return;
+            }
+
+            spec.streamProperties(MixedIn.EXCLUDED)
+            // skip checks if annotated with JDO @NotPersistent
+            .filter(association->!association.containsNonFallbackFacet(JdoNotPersistentFacet.class))
+            .forEach(BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil::validateBigDecimalValueFacet);
+
+        });
+    }
+
+}
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromAbsenceOfColumnAnnotation.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromAbsenceOfJdoColumnAnnotation.java
similarity index 91%
rename from persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromAbsenceOfColumnAnnotation.java
rename to persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromAbsenceOfJdoColumnAnnotation.java
index 985adbec7f..7c7c324497 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromAbsenceOfColumnAnnotation.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromAbsenceOfJdoColumnAnnotation.java
@@ -24,10 +24,10 @@ import org.apache.causeway.core.metamodel.facets.objectvalue.mandatory.Mandatory
 /**
  * Inferred from absence of an <tt>@Column</tt> method.
  */
-public class MandatoryFacetFromAbsenceOfColumnAnnotation
+public class MandatoryFacetFromAbsenceOfJdoColumnAnnotation
 extends MandatoryFacetAbstract {
 
-    public MandatoryFacetFromAbsenceOfColumnAnnotation(
+    public MandatoryFacetFromAbsenceOfJdoColumnAnnotation(
             final Semantics semantics, final FacetHolder holder, final Precedence precedence) {
         super(semantics, holder, precedence);
     }
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromColumnAnnotation.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromJdoColumnAnnotation.java
similarity index 92%
rename from persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromColumnAnnotation.java
rename to persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromJdoColumnAnnotation.java
index 5e159b19db..fdb271f6ca 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromColumnAnnotation.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFacetFromJdoColumnAnnotation.java
@@ -24,10 +24,10 @@ import org.apache.causeway.core.metamodel.facets.objectvalue.mandatory.Mandatory
 /**
  * Inferred from presence of an <tt>@Column</tt> annotation.
  */
-public class MandatoryFacetFromColumnAnnotation
+public class MandatoryFacetFromJdoColumnAnnotation
 extends MandatoryFacetAbstract {
 
-    public MandatoryFacetFromColumnAnnotation(
+    public MandatoryFacetFromJdoColumnAnnotation(
             final Semantics semantics, final FacetHolder holder) {
         super(semantics, holder);
     }
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromColumnAnnotationFacetFactory.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java
similarity index 68%
rename from persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromColumnAnnotationFacetFactory.java
rename to persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java
index 8bbdd0b0e8..e48052e83b 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromColumnAnnotationFacetFactory.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactory.java
@@ -18,11 +18,16 @@
  */
 package org.apache.causeway.persistence.jdo.metamodel.facets.prop.column;
 
+import lombok.val;
+
+import java.util.Optional;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
+import javax.jdo.annotations.Column;
 import javax.jdo.annotations.IdentityType;
 
+import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.Facet.Precedence;
 import org.apache.causeway.core.metamodel.facetapi.FacetUtil;
@@ -35,21 +40,21 @@ import org.apache.causeway.core.metamodel.facets.properties.property.mandatory.M
 import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
 import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
-import org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
+import org.apache.causeway.persistence.commons.metamodel.facets.prop.column.MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil;
 import org.apache.causeway.persistence.jdo.metamodel.facets.prop.primarykey.MandatoryFacetFromJdoPrimaryKeyAnnotation;
 import org.apache.causeway.persistence.jdo.provider.entities.JdoFacetContext;
 import org.apache.causeway.persistence.jdo.provider.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
 import org.apache.causeway.persistence.jdo.provider.metamodel.facets.prop.notpersistent.JdoNotPersistentFacet;
 
 
-public class MandatoryFromColumnAnnotationFacetFactory
+public class MandatoryFromJdoColumnAnnotationFacetFactory
 extends FacetFactoryAbstract
 implements MetaModelRefiner {
 
     private final JdoFacetContext jdoFacetContext;
 
     @Inject
-    public MandatoryFromColumnAnnotationFacetFactory(
+    public MandatoryFromJdoColumnAnnotationFacetFactory(
             final MetaModelContext mmc,
             final JdoFacetContext jdoFacetContext) {
         super(mmc, FeatureType.PROPERTIES_ONLY);
@@ -82,23 +87,46 @@ implements MetaModelRefiner {
             }
         }
 
-        _ColumnUtil.inferSemantics(processMethodContext,
-                semanticsWhileColumnPresent->{
-                    FacetUtil.addFacet(
-                            new MandatoryFacetFromColumnAnnotation(semanticsWhileColumnPresent, holder));
-                },
-                semanticsWhileColumnAbsent->{
-                    FacetUtil.addFacet(
-                            new MandatoryFacetFromAbsenceOfColumnAnnotation(
-                                    semanticsWhileColumnAbsent,
-                                    holder,
-                                    semanticsWhileColumnAbsent.isRequired()
-                                        ? Precedence.DEFAULT
-                                        : Precedence.INFERRED));
-                });
+        val jdoColumnIfAny = processMethodContext.synthesizeOnMethod(javax.jdo.annotations.Column.class);
+        MandatoryFacet.Semantics semantics = inferSemantics(processMethodContext, jdoColumnIfAny);
+        if(jdoColumnIfAny.isPresent()) {
+            FacetUtil.addFacet(
+                    new MandatoryFacetFromJdoColumnAnnotation(semantics, holder));
+        } else {
+            FacetUtil.addFacet(
+                    new MandatoryFacetFromAbsenceOfJdoColumnAnnotation(
+                            semantics,
+                            holder,
+                            semantics.isRequired()
+                                    ? Precedence.DEFAULT
+                                    : Precedence.INFERRED));
+        }
 
     }
 
+    static MandatoryFacet.Semantics inferSemantics(
+            final ProcessMethodContext processMethodContext,
+            final Optional<Column> columnIfAny) {
+
+        final String allowsNull = columnIfAny.isPresent()
+                ? columnIfAny.get().allowsNull()
+                : null;
+
+        if(_Strings.isNotEmpty(allowsNull)) {
+            // if miss-spelled, then DN assumes is not-nullable
+            return MandatoryFacet.Semantics.required(!"true".equalsIgnoreCase(allowsNull.trim()));
+        }
+
+        final Class<?> returnType = processMethodContext.getMethod().getReturnType();
+        // per JDO spec
+        return returnType != null
+                && returnType.isPrimitive()
+                ? MandatoryFacet.Semantics.REQUIRED
+                : MandatoryFacet.Semantics.OPTIONAL;
+
+    }
+
+
     @Override
     public void refineProgrammingModel(final ProgrammingModel programmingModel) {
         programmingModel.addValidatorSkipManagedBeans(objectSpec->{
@@ -115,26 +143,9 @@ implements MetaModelRefiner {
             associations
             // skip checks if annotated with JDO @NotPersistent
             .filter(association->!association.containsNonFallbackFacet(JdoNotPersistentFacet.class))
-            .forEach(association->validateMandatoryFacet(association));
+            .forEach(MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil::validateMandatoryFacet);
 
         });
     }
 
-    private static void validateMandatoryFacet(final ObjectAssociation association) {
-
-        association.lookupFacet(MandatoryFacet.class)
-        .map(MandatoryFacet::getSharedFacetRankingElseFail)
-        .ifPresent(facetRanking->facetRanking
-                .visitTopRankPairsSemanticDiffering(MandatoryFacet.class, (a, b)->{
-
-                    ValidationFailure.raiseFormatted(
-                            association,
-                            "%s: inconsistent Mandatory/Optional semantics specified in %s and %s.",
-                            association.getFeatureIdentifier().toString(),
-                            a.getClass().getSimpleName(),
-                            b.getClass().getSimpleName());
-                }));
-
-    }
-
 }
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumn.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumnAnnotation.java
similarity index 69%
copy from persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumn.java
copy to persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumnAnnotation.java
index b150bef8cd..a20e3eb613 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumn.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumnAnnotation.java
@@ -26,37 +26,24 @@ import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxFractionalDigitsFacet;
 import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxFractionalDigitsFacetAbstract;
 
-public class MaxFractionalDigitsFacetFromJdoColumn
+public class MaxFractionalDigitsFacetFromJdoColumnAnnotation
 extends MaxFractionalDigitsFacetAbstract {
 
-    public static Optional<MaxFractionalDigitsFacet> createJdo(
+    public static Optional<MaxFractionalDigitsFacet> create(
             final Optional<Column> jdoColumnIfAny,
             final FacetHolder holder) {
 
         return jdoColumnIfAny
                 .filter(jdoColumn->jdoColumn.scale()>=0)
                 .map(jdoColumn->{
-                    return new MaxFractionalDigitsFacetFromJdoColumn(
+                    return new MaxFractionalDigitsFacetFromJdoColumnAnnotation(
                             jdoColumn.scale(), holder);
                 });
     }
 
-    public static Optional<MaxFractionalDigitsFacet> createJpa(
-            final Optional<javax.persistence.Column> jdoColumnIfAny,
-            final FacetHolder holder) {
-
-        return jdoColumnIfAny
-                .filter(jdoColumn->jdoColumn.scale()>=0)
-                .map(jdoColumn->{
-                    return new MaxFractionalDigitsFacetFromJdoColumn(
-                            jdoColumn.scale(), holder);
-                });
+    private MaxFractionalDigitsFacetFromJdoColumnAnnotation(
+            final int scale, final FacetHolder holder) {
+        super(scale, holder);
     }
 
-    private MaxFractionalDigitsFacetFromJdoColumn(
-            final int maxFractionalDigits, final FacetHolder holder) {
-        super(maxFractionalDigits, holder);
-    }
-
-
 }
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java
index 4ca01d5fbb..5dfb363f84 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java
@@ -29,7 +29,7 @@ import org.apache.causeway.core.metamodel.facets.objectvalue.maxlen.MaxLengthFac
 public class MaxLengthFacetFromJdoColumnAnnotation
 extends MaxLengthFacetAbstract {
 
-    public static Optional<MaxLengthFacet> createJdo(
+    public static Optional<MaxLengthFacet> create(
             final Optional<Column> jdoColumnIfAny,
             final FacetHolder holder) {
 
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFromJdoColumnAnnotationFacetFactory.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFromJdoColumnAnnotationFacetFactory.java
index 6d330e0c19..c8fbd6ea96 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFromJdoColumnAnnotationFacetFactory.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFromJdoColumnAnnotationFacetFactory.java
@@ -21,6 +21,7 @@ package org.apache.causeway.persistence.jdo.metamodel.facets.prop.column;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
+import javax.jdo.annotations.Column;
 import javax.jdo.annotations.IdentityType;
 
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
@@ -64,18 +65,11 @@ implements MetaModelRefiner {
 
         val facetHolder = processMethodContext.getFacetHolder();
 
-        _ColumnUtil.processColumnAnnotations(processMethodContext,
-                jdoColumnIfAny->{
-                    FacetUtil.addFacetIfPresent(
-                            MaxTotalDigitsFacetFromJdoColumnAnnotation
-                            .createJdo(jdoColumnIfAny, facetHolder));
-                },
-                jpaColumnIfAny->{
-                    FacetUtil.addFacetIfPresent(
-                            MaxTotalDigitsFacetFromJdoColumnAnnotation
-                            .createJpa(jpaColumnIfAny, facetHolder));
-                });
+        val jdoColumnIfAny = processMethodContext.synthesizeOnMethod(Column.class);
 
+        FacetUtil.addFacetIfPresent(
+                MaxLengthFacetFromJdoColumnAnnotation
+                .create(jdoColumnIfAny, facetHolder));
     }
 
     @Override
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumn.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MinFractionalDigitsFacetFromJdoColumnAnnotation.java
similarity index 61%
rename from persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumn.java
rename to persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MinFractionalDigitsFacetFromJdoColumnAnnotation.java
index b150bef8cd..faf1ea497f 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJdoColumn.java
+++ b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MinFractionalDigitsFacetFromJdoColumnAnnotation.java
@@ -23,39 +23,27 @@ import java.util.Optional;
 import javax.jdo.annotations.Column;
 
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
-import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxFractionalDigitsFacet;
-import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MaxFractionalDigitsFacetAbstract;
+import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MinFractionalDigitsFacet;
+import org.apache.causeway.core.metamodel.facets.objectvalue.digits.MinFractionalDigitsFacetAbstract;
 
-public class MaxFractionalDigitsFacetFromJdoColumn
-extends MaxFractionalDigitsFacetAbstract {
+public class MinFractionalDigitsFacetFromJdoColumnAnnotation
+extends MinFractionalDigitsFacetAbstract {
 
-    public static Optional<MaxFractionalDigitsFacet> createJdo(
+    public static Optional<MinFractionalDigitsFacet> create(
             final Optional<Column> jdoColumnIfAny,
             final FacetHolder holder) {
 
         return jdoColumnIfAny
                 .filter(jdoColumn->jdoColumn.scale()>=0)
                 .map(jdoColumn->{
-                    return new MaxFractionalDigitsFacetFromJdoColumn(
+                    return new MinFractionalDigitsFacetFromJdoColumnAnnotation(
                             jdoColumn.scale(), holder);
                 });
     }
 
-    public static Optional<MaxFractionalDigitsFacet> createJpa(
-            final Optional<javax.persistence.Column> jdoColumnIfAny,
-            final FacetHolder holder) {
-
-        return jdoColumnIfAny
-                .filter(jdoColumn->jdoColumn.scale()>=0)
-                .map(jdoColumn->{
-                    return new MaxFractionalDigitsFacetFromJdoColumn(
-                            jdoColumn.scale(), holder);
-                });
-    }
-
-    private MaxFractionalDigitsFacetFromJdoColumn(
-            final int maxFractionalDigits, final FacetHolder holder) {
-        super(maxFractionalDigits, holder);
+    private MinFractionalDigitsFacetFromJdoColumnAnnotation(
+            final int scale, final FacetHolder holder) {
+        super(scale, holder);
     }
 
 
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/_ColumnUtil.java b/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/_ColumnUtil.java
deleted file mode 100644
index 89b95503a5..0000000000
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/_ColumnUtil.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.causeway.persistence.jdo.metamodel.facets.prop.column;
-
-import java.util.Optional;
-import java.util.function.Consumer;
-
-import javax.jdo.annotations.Column;
-
-import org.apache.causeway.commons.internal.base._Strings;
-import org.apache.causeway.core.metamodel.facets.FacetFactory.ProcessMethodContext;
-import org.apache.causeway.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet.Semantics;
-
-import lombok.val;
-import lombok.experimental.UtilityClass;
-
-@UtilityClass
-class _ColumnUtil {
-
-    void processColumnAnnotations(final ProcessMethodContext processMethodContext,
-            final Consumer<Optional<javax.jdo.annotations.Column>> onJdoColumn,
-            final Consumer<Optional<javax.persistence.Column>> onJpaColumn) {
-
-        val jdoColumnIfAny = processMethodContext.synthesizeOnMethod(javax.jdo.annotations.Column.class);
-        if(jdoColumnIfAny.isPresent()) {
-            onJdoColumn.accept(jdoColumnIfAny);
-        } else {
-            val jpaColumnIfAny = processMethodContext.synthesizeOnMethod(javax.persistence.Column.class);
-            if(jpaColumnIfAny.isPresent()) {
-                onJpaColumn.accept(jpaColumnIfAny);
-            }
-        }
-
-    }
-
-    void inferSemantics(final ProcessMethodContext processMethodContext,
-            final Consumer<Semantics> onColumnPresent,
-            final Consumer<Semantics> onColumnNotPresent) {
-        val jdoColumnIfAny = processMethodContext.synthesizeOnMethod(javax.jdo.annotations.Column.class);
-        if(jdoColumnIfAny.isPresent()) {
-            onColumnPresent.accept(inferSemantics(processMethodContext, jdoColumnIfAny));
-            return;
-        }
-        val jpaColumnIfAny = processMethodContext.synthesizeOnMethod(javax.persistence.Column.class);
-        if(jpaColumnIfAny.isPresent()) {
-            onColumnPresent.accept(Semantics.required(!jpaColumnIfAny.get().nullable()));
-            return;
-        }
-        onColumnNotPresent.accept(inferSemantics(processMethodContext, Optional.empty()));
-    }
-
-    // -- HELPER
-
-    private Semantics inferSemantics(
-            final ProcessMethodContext processMethodContext,
-            final Optional<Column> columnIfAny) {
-
-        final String allowsNull = columnIfAny.isPresent()
-                ? columnIfAny.get().allowsNull()
-                : null;
-
-        if(_Strings.isNotEmpty(allowsNull)) {
-            // if miss-spelled, then DN assumes is not-nullable
-            return Semantics.required(!"true".equalsIgnoreCase(allowsNull.trim()));
-        }
-
-        final Class<?> returnType = processMethodContext.getMethod().getReturnType();
-        // per JDO spec
-        return returnType != null
-                && returnType.isPrimitive()
-            ? Semantics.REQUIRED
-            : Semantics.OPTIONAL;
-
-    }
-
-}
diff --git a/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactoryTest.java b/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactoryTest.java
index 15e135aa5b..bc28d1ab06 100644
--- a/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactoryTest.java
+++ b/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/BigDecimalFromJdoColumnAnnotationFacetFactoryTest.java
@@ -38,14 +38,14 @@ import lombok.val;
 class BigDecimalFromJdoColumnAnnotationFacetFactoryTest
 extends AbstractFacetFactoryTest {
 
-    private BigDecimalFromColumnAnnotationFacetFactory facetFactory;
+    private BigDecimalFromJdoColumnAnnotationFacetFactory facetFactory;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
         val mmc = MetaModelContext_forTesting.buildDefault();
-        facetFactory = new BigDecimalFromColumnAnnotationFacetFactory(mmc);
+        facetFactory = new BigDecimalFromJdoColumnAnnotationFacetFactory(mmc);
     }
 
     @Override
@@ -115,7 +115,7 @@ extends AbstractFacetFactoryTest {
         if(maxFractionalDigits>=0) {
             final MaxFractionalDigitsFacet facet = facetedMethod.getFacet(MaxFractionalDigitsFacet.class);
             assertNotNull(facet);
-            assertTrue(facet instanceof MaxFractionalDigitsFacetFromJdoColumn);
+            assertTrue(facet instanceof MaxFractionalDigitsFacetFromJdoColumnAnnotation);
             assertThat(facet.getMaxFractionalDigits(), is(maxFractionalDigits));
         } else {
             assertNull(facetedMethod.getFacet(MaxFractionalDigitsFacet.class));
diff --git a/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactoryTest.java b/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactoryTest.java
index 4d9d3199dd..440bf3f65c 100644
--- a/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactoryTest.java
+++ b/persistence/jdo/metamodel/src/test/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MandatoryFromJdoColumnAnnotationFacetFactoryTest.java
@@ -34,14 +34,14 @@ import lombok.val;
 class MandatoryFromJdoColumnAnnotationFacetFactoryTest
 extends AbstractFacetFactoryTest {
 
-    private MandatoryFromColumnAnnotationFacetFactory facetFactory;
+    private MandatoryFromJdoColumnAnnotationFacetFactory facetFactory;
     private Class<?> cls;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        facetFactory = new MandatoryFromColumnAnnotationFacetFactory(metaModelContext, jdoFacetContext);
+        facetFactory = new MandatoryFromJdoColumnAnnotationFacetFactory(metaModelContext, jdoFacetContext);
         cls = SimpleObjectWithColumnAllowsNullAnnotations.class;
     }
 
@@ -67,7 +67,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromAbsenceOfColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromAbsenceOfJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(false));
     }
 
@@ -78,7 +78,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(false));
     }
 
@@ -89,7 +89,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(false));
     }
 
@@ -100,7 +100,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(true));
     }
 
@@ -111,7 +111,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromAbsenceOfColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromAbsenceOfJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(true));
     }
 
@@ -122,7 +122,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(true));
     }
 
@@ -133,7 +133,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(false));
     }
 
@@ -144,7 +144,7 @@ extends AbstractFacetFactoryTest {
 
         final MandatoryFacet facet = facetedMethod.getFacet(MandatoryFacet.class);
         assertNotNull(facet);
-        assertTrue(facet instanceof MandatoryFacetFromColumnAnnotation);
+        assertTrue(facet instanceof MandatoryFacetFromJdoColumnAnnotation);
         assertThat(facet.getSemantics().isOptional(), is(true));
     }
 
diff --git a/persistence/jpa/metamodel/pom.xml b/persistence/jpa/metamodel/pom.xml
index e97a9d63d1..9f782b171c 100644
--- a/persistence/jpa/metamodel/pom.xml
+++ b/persistence/jpa/metamodel/pom.xml
@@ -1,13 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 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 
+<!-- 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. -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
@@ -37,14 +37,19 @@
 			<groupId>org.apache.causeway.persistence</groupId>
 			<artifactId>causeway-persistence-jpa-applib</artifactId>
 		</dependency>
-		
-		<dependency>
+
+        <dependency>
+            <groupId>org.apache.causeway.persistence</groupId>
+            <artifactId>causeway-persistence-commons</artifactId>
+        </dependency>
+
+        <dependency>
 			<groupId>org.apache.causeway.core</groupId>
 			<artifactId>causeway-core-runtime</artifactId>
 		</dependency>
 
 		<!-- TESTING -->
-		
+
         <dependency>
             <groupId>org.apache.causeway.core</groupId>
             <artifactId>causeway-core-internaltestsupport</artifactId>
diff --git a/persistence/jpa/metamodel/src/main/java/module-info.java b/persistence/jpa/metamodel/src/main/java/module-info.java
index ad5d858e20..950bebd715 100644
--- a/persistence/jpa/metamodel/src/main/java/module-info.java
+++ b/persistence/jpa/metamodel/src/main/java/module-info.java
@@ -32,4 +32,5 @@ module org.apache.causeway.persistence.jpa.metamodel {
     requires org.apache.causeway.core.runtime;
     requires org.apache.causeway.persistence.jpa.applib;
     requires spring.context;
+    requires org.apache.causeway.persistence.commons;
 }
\ No newline at end of file
diff --git a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/JpaProgrammingModel.java b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/JpaProgrammingModel.java
index 138b6f190a..a01d47ce50 100644
--- a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/JpaProgrammingModel.java
+++ b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/JpaProgrammingModel.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.persistence.jpa.metamodel;
 
+import org.apache.causeway.persistence.jpa.metamodel.facets.prop.column.MaxLengthFromJpaColumnAnnotationFacetFactory;
+
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
@@ -45,6 +47,7 @@ public class JpaProgrammingModel implements MetaModelRefiner {
         pm.addFactory(step, new JpaTransientAnnotationFacetFactory(mmc), Marker.JPA);
         pm.addFactory(step, new MandatoryFromJpaColumnAnnotationFacetFactory(mmc), Marker.JPA);
         pm.addFactory(step, new BigDecimalFromJpaColumnAnnotationFacetFactory(mmc), Marker.JPA);
+        pm.addFactory(step, new MaxLengthFromJpaColumnAnnotationFacetFactory(mmc), Marker.JPA);
 
     }
 
diff --git a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/BigDecimalFromJpaColumnAnnotationFacetFactory.java b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/BigDecimalFromJpaColumnAnnotationFacetFactory.java
index 583b32b06e..f79bda9c7d 100644
--- a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/BigDecimalFromJpaColumnAnnotationFacetFactory.java
+++ b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/BigDecimalFromJpaColumnAnnotationFacetFactory.java
@@ -25,13 +25,18 @@ import javax.persistence.Column;
 
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.FeatureType;
+import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
 import org.apache.causeway.core.metamodel.facets.FacetFactoryAbstract;
 import org.apache.causeway.core.metamodel.facets.FacetedMethod;
+import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.persistence.commons.metamodel.facets.prop.column.BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil;
 
 import lombok.val;
 
 public class BigDecimalFromJpaColumnAnnotationFacetFactory
-extends FacetFactoryAbstract {
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
 
     @Inject
     public BigDecimalFromJpaColumnAnnotationFacetFactory(final MetaModelContext mmc) {
@@ -59,4 +64,14 @@ extends FacetFactoryAbstract {
 
     }
 
+    @Override
+    public void refineProgrammingModel(final ProgrammingModel programmingModel) {
+        programmingModel.addValidatorSkipManagedBeans(spec->{
+
+            spec.streamProperties(MixedIn.EXCLUDED)
+                    .forEach(BigDecimalFromXxxColumnAnnotationMetaModelRefinerUtil::validateBigDecimalValueFacet);
+
+        });
+    }
+
 }
diff --git a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java
index e24ef5951a..94597ac206 100644
--- a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java
+++ b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java
@@ -19,6 +19,7 @@
 package org.apache.causeway.persistence.jpa.metamodel.facets.prop.column;
 
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.persistence.Column;
@@ -27,13 +28,19 @@ import javax.persistence.JoinColumn;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.FacetUtil;
 import org.apache.causeway.core.metamodel.facetapi.FeatureType;
+import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
 import org.apache.causeway.core.metamodel.facets.FacetFactoryAbstract;
 import org.apache.causeway.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet.Semantics;
+import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.persistence.commons.metamodel.facets.prop.column.MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil;
 
 import lombok.val;
 
 public class MandatoryFromJpaColumnAnnotationFacetFactory
-extends FacetFactoryAbstract {
+extends FacetFactoryAbstract
+implements MetaModelRefiner {
 
     @Inject
     public MandatoryFromJpaColumnAnnotationFacetFactory(final MetaModelContext mmc) {
@@ -43,8 +50,6 @@ extends FacetFactoryAbstract {
     @Override
     public void process(final ProcessMethodContext processMethodContext) {
 
-        //val cls = processMethodContext.getCls();
-
         final Optional<Boolean> nullable1 = processMethodContext.synthesizeOnMethod(JoinColumn.class)
                 .map(JoinColumn::nullable);
 
@@ -64,5 +69,18 @@ extends FacetFactoryAbstract {
                 new MandatoryFacetFromJpaColumnAnnotation(semantics, facetHolder));
     }
 
+    @Override
+    public void refineProgrammingModel(final ProgrammingModel programmingModel) {
+        programmingModel.addValidatorSkipManagedBeans(objectSpec->{
+
+            final Stream<ObjectAssociation> associations = objectSpec
+                    .streamAssociations(MixedIn.EXCLUDED)
+                    .filter(ObjectAssociation.Predicates.PROPERTIES);
+
+            associations
+                    .forEach(MandatoryFromXxxColumnAnnotationMetaModelRefinerUtil::validateMandatoryFacet);
+
+        });
+    }
 
 }
diff --git a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJpaColumnAnnotation.java b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJpaColumnAnnotation.java
index 84b3677ced..38a994625b 100644
--- a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJpaColumnAnnotation.java
+++ b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxFractionalDigitsFacetFromJpaColumnAnnotation.java
@@ -35,15 +35,13 @@ extends MaxFractionalDigitsFacetAbstract {
 
          return jpaColumnIfAny
          .filter(jpaColumn->jpaColumn.scale()>=0)
-         .map(jdoColumn->{
-             return new MaxFractionalDigitsFacetFromJpaColumnAnnotation(
-                     jdoColumn.scale(), holder);
-         });
+         .map(jdoColumn-> new MaxFractionalDigitsFacetFromJpaColumnAnnotation(
+                 jdoColumn.scale(), holder));
     }
 
     private MaxFractionalDigitsFacetFromJpaColumnAnnotation(
-            final int maxFractionalDigits, final FacetHolder holder) {
-        super(maxFractionalDigits, holder);
+            final int scale, final FacetHolder holder) {
+        super(scale, holder);
     }
 
 }
diff --git a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxLengthFacetFromJpaColumnAnnotation.java
similarity index 66%
copy from persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java
copy to persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxLengthFacetFromJpaColumnAnnotation.java
index 4ca01d5fbb..028fb62a71 100644
--- a/persistence/jdo/metamodel/src/main/java/org/apache/causeway/persistence/jdo/metamodel/facets/prop/column/MaxLengthFacetFromJdoColumnAnnotation.java
+++ b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxLengthFacetFromJpaColumnAnnotation.java
@@ -16,40 +16,30 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.causeway.persistence.jdo.metamodel.facets.prop.column;
+package org.apache.causeway.persistence.jpa.metamodel.facets.prop.column;
 
 import java.util.Optional;
 
-import javax.jdo.annotations.Column;
+import javax.persistence.Column;
 
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
 import org.apache.causeway.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacetAbstract;
 
-public class MaxLengthFacetFromJdoColumnAnnotation
+public class MaxLengthFacetFromJpaColumnAnnotation
 extends MaxLengthFacetAbstract {
 
-    public static Optional<MaxLengthFacet> createJdo(
-            final Optional<Column> jdoColumnIfAny,
-            final FacetHolder holder) {
-
-        return jdoColumnIfAny
-        .map(jdoColumn->
-            new MaxLengthFacetFromJdoColumnAnnotation(
-                    jdoColumn.length(), holder));
-    }
-
-    public static Optional<MaxLengthFacet> createJpa(
-            final Optional<javax.persistence.Column> jpaColumnIfAny,
+    public static Optional<MaxLengthFacet> create(
+            final Optional<Column> jpaColumnIfAny,
             final FacetHolder holder) {
 
         return jpaColumnIfAny
         .map(jdoColumn->
-            new MaxLengthFacetFromJdoColumnAnnotation(
+            new MaxLengthFacetFromJpaColumnAnnotation(
                     jdoColumn.length(), holder));
     }
 
-    private MaxLengthFacetFromJdoColumnAnnotation(
+    private MaxLengthFacetFromJpaColumnAnnotation(
             final int maxLength, final FacetHolder holder) {
         super(maxLength, holder);
     }
diff --git a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxLengthFromJpaColumnAnnotationFacetFactory.java
similarity index 59%
copy from persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java
copy to persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxLengthFromJpaColumnAnnotationFacetFactory.java
index e24ef5951a..5df9e0cf0d 100644
--- a/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MandatoryFromJpaColumnAnnotationFacetFactory.java
+++ b/persistence/jpa/metamodel/src/main/java/org/apache/causeway/persistence/jpa/metamodel/facets/prop/column/MaxLengthFromJpaColumnAnnotationFacetFactory.java
@@ -18,51 +18,47 @@
  */
 package org.apache.causeway.persistence.jpa.metamodel.facets.prop.column;
 
-import java.util.Optional;
+import java.util.stream.Stream;
 
-import javax.inject.Inject;
 import javax.persistence.Column;
-import javax.persistence.JoinColumn;
 
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.facetapi.FacetUtil;
 import org.apache.causeway.core.metamodel.facetapi.FeatureType;
+import org.apache.causeway.core.metamodel.facetapi.MetaModelRefiner;
 import org.apache.causeway.core.metamodel.facets.FacetFactoryAbstract;
-import org.apache.causeway.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet.Semantics;
+import org.apache.causeway.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
+import org.apache.causeway.core.metamodel.progmodel.ProgrammingModel;
+import org.apache.causeway.core.metamodel.spec.feature.MixedIn;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.causeway.core.metamodel.specloader.validator.ValidationFailure;
 
 import lombok.val;
 
-public class MandatoryFromJpaColumnAnnotationFacetFactory
+public class MaxLengthFromJpaColumnAnnotationFacetFactory
 extends FacetFactoryAbstract {
 
-    @Inject
-    public MandatoryFromJpaColumnAnnotationFacetFactory(final MetaModelContext mmc) {
+    public MaxLengthFromJpaColumnAnnotationFacetFactory(final MetaModelContext mmc) {
         super(mmc, FeatureType.PROPERTIES_ONLY);
     }
 
     @Override
     public void process(final ProcessMethodContext processMethodContext) {
 
-        //val cls = processMethodContext.getCls();
+        // only applies to JDO entities; ignore any view models
+        final Class<?> cls = processMethodContext.getCls();
 
-        final Optional<Boolean> nullable1 = processMethodContext.synthesizeOnMethod(JoinColumn.class)
-                .map(JoinColumn::nullable);
-
-        final Optional<Boolean> nullable2 = processMethodContext.synthesizeOnMethod(Column.class)
-                .map(Column::nullable);
-
-        if(!nullable1.isPresent()
-                && !nullable2.isPresent()) {
+        if(String.class != processMethodContext.getMethod().getReturnType()) {
             return;
         }
 
-        val nullable = nullable1.orElseGet(nullable2::get);
-        val semantics = Semantics.required(!nullable);
-
         val facetHolder = processMethodContext.getFacetHolder();
-        FacetUtil.addFacet(
-                new MandatoryFacetFromJpaColumnAnnotation(semantics, facetHolder));
-    }
 
+        val jdoColumnIfAny = processMethodContext.synthesizeOnMethod(Column.class);
+
+        FacetUtil.addFacetIfPresent(
+                MaxLengthFacetFromJpaColumnAnnotation
+                .create(jdoColumnIfAny, facetHolder));
+    }
 
 }