You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2022/05/20 10:48:22 UTC

[isis] 01/02: ISIS-2651: adds docs for JdoEntityDiscoveryListener, polishes its AP

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

danhaywood pushed a commit to branch ISIS-2651
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 04781715e9cf8565b6b20f9c6ccf283de6d4437b
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Fri May 20 11:44:18 2022 +0100

    ISIS-2651: adds docs for JdoEntityDiscoveryListener, polishes its AP
    
    also docs on using p6spy for tracing
---
 .../isis/core/config/DatanucleusConfiguration.java | 14 ++--
 .../modules/ROOT/pages/dependency-injection.adoc   | 17 ++++-
 .../adoc/modules/ROOT/pages/hints-and-tips.adoc    |  4 +-
 .../hints-and-tips/tracing-sql-statements.adoc     | 75 ++++++++++++++++++++++
 .../ROOT/pages/setup-and-configuration.adoc        | 14 ++--
 .../pages/{hints-and-tips/java8.adoc => spis.adoc} | 27 +-------
 .../pages/spis/JdoEntityDiscoveryListener.adoc     | 69 ++++++++++++++++++++
 .../jdo/adoc/modules/ROOT/partials/module-nav.adoc |  1 +
 .../IsisModulePersistenceJdoDatanucleus.java       | 29 ++++-----
 .../{DnSettings.java => DatanucleusSettings.java}  | 49 ++++++++++----
 .../jdosupport/JdoSupportServiceDefault.java       |  2 +-
 .../config/JdoEntityDiscoveryListener.java         | 10 ++-
 12 files changed, 238 insertions(+), 73 deletions(-)

diff --git a/core/config/src/main/java/org/apache/isis/core/config/DatanucleusConfiguration.java b/core/config/src/main/java/org/apache/isis/core/config/DatanucleusConfiguration.java
index 3d830726c7..1fe489822b 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/DatanucleusConfiguration.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/DatanucleusConfiguration.java
@@ -150,7 +150,7 @@ public class DatanucleusConfiguration {
          * constraints that don’t exist.
          *
          * <p>
-         *     For integration testing, it's generally better to use {@link Generatedatabase#setMode(String) datanucleus.schema.generateDatabase.mode},
+         *     For integration testing, it's generally better to use {@link GenerateDatabase#setMode(String) datanucleus.schema.generateDatabase.mode},
          *     which will eagerly create all tables on startup.
          * </p>
          *
@@ -164,7 +164,7 @@ public class DatanucleusConfiguration {
          * @see #setAutoCreateColumns(boolean)
          * @see #setAutoCreateConstraints(boolean)
          * @see #setAutoCreateTables(boolean)
-         * @see Generatedatabase#setMode(String)
+         * @see GenerateDatabase#setMode(String)
          */
         private boolean autoCreateAll = false;
 
@@ -195,7 +195,7 @@ public class DatanucleusConfiguration {
          * @see #setAutoCreateAll(boolean)
          * @see #setAutoCreateColumns(boolean)
          * @see #setAutoCreateConstraints(boolean)
-         * @see Generatedatabase#setMode(String)
+         * @see GenerateDatabase#setMode(String)
          */
         private boolean autoCreateTables = false;
 
@@ -212,7 +212,7 @@ public class DatanucleusConfiguration {
          * @see #setAutoCreateAll(boolean)
          * @see #setAutoCreateConstraints(boolean)
          * @see #setAutoCreateTables(boolean)
-         * @see Generatedatabase#setMode(String)
+         * @see GenerateDatabase#setMode(String)
          */
         private boolean autoCreateColumns = false;
 
@@ -229,7 +229,7 @@ public class DatanucleusConfiguration {
          * @see #setAutoCreateAll(boolean)
          * @see #setAutoCreateColumns(boolean)
          * @see #setAutoCreateTables(boolean)
-         * @see Generatedatabase#setMode(String)
+         * @see GenerateDatabase#setMode(String)
          */
         private boolean autoCreateConstraints = false;
 
@@ -285,9 +285,9 @@ public class DatanucleusConfiguration {
          */
         private boolean validateAll = false;
 
-        private final Generatedatabase generatedatabase = new Generatedatabase();
+        private final GenerateDatabase generateDatabase = new GenerateDatabase();
         @Data
-        public static class Generatedatabase {
+        public static class GenerateDatabase {
 
             /**
              * Whether to eagerly create all tables at startup.
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/dependency-injection.adoc b/persistence/jdo/adoc/modules/ROOT/pages/dependency-injection.adoc
index af6be704d6..edec24230a 100644
--- a/persistence/jdo/adoc/modules/ROOT/pages/dependency-injection.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/pages/dependency-injection.adoc
@@ -5,5 +5,20 @@
 
 With Apache Isis it's common to inject domain services into domain entities; this allows behaviour to be pushed down into those entities.
 
-For JDO, this injection performs automatically; there is no configuration or boilerplate required.
+For JDO, both field injection and setter injection is performed automatically:
+
+[source,java]
+.Customer.java
+----
+import javax.inject.Inject;
+
+// ...
+public class Customer {
+
+    // ...
+    @Import OrderRepository orderRepository;
+}
+----
+
+However, constructor injection is _not_ supported.
 
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips.adoc b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips.adoc
index 121de6029d..ab963dc54b 100644
--- a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips.adoc
@@ -4,11 +4,11 @@
 :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 [...]
 
 
-This chapter provides some solutions for problems we've encountered ourselves or have been raised on the Apache Isis mailing lists.
+This page provides some solutions for problems we've encountered ourselves or have been raised on the Apache Isis mailing lists.
 
+include::hints-and-tips/tracing-sql-statements.adoc[leveloffset=+1]
 include::hints-and-tips/overriding-jdo-annotations.adoc[leveloffset=+1]
 include::hints-and-tips/subtype-entity-not-fully-populated.adoc[leveloffset=+1]
-include::hints-and-tips/java8.adoc[leveloffset=+1]
 include::hints-and-tips/diagnosing-n-plus-1.adoc[leveloffset=+1]
 include::hints-and-tips/typesafe-queries-and-fetchgroups.adoc[leveloffset=+1]
 include::hints-and-tips/jdoql-and-timestamps.adoc[leveloffset=+1]
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/tracing-sql-statements.adoc b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/tracing-sql-statements.adoc
new file mode 100644
index 0000000000..bc6f84ad92
--- /dev/null
+++ b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/tracing-sql-statements.adoc
@@ -0,0 +1,75 @@
+= Tracing SQL Statements
+
+: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 [...]
+
+
+If you want to debug the SQL being sent to the database, one approach is to use the link:https://github.com/p6spy/p6spy[p6spy] JDBC Driver.
+This acts as a decorator, logging the calls and then passing through to the underlying database.
+
+Enabling p6spy is easily done using the https://github.com/gavlyukovskiy/spring-boot-data-source-decorator[gavlyukovskiy/spring-boot-data-source-decorator] package:
+
+* add the `com.github.gavlyukovskiy:p6spy-spring-boot-starter` dependency to your `pom.xml`:
++
+[source,xml]
+.pom.xml
+----
+<dependency>
+    <groupId>com.github.gavlyukovskiy</groupId>
+    <artifactId>p6spy-spring-boot-starter</artifactId>
+    <version>1.7.1</version>
+    <scope>compile</scope>      <!--.-->
+</dependency>
+----
+<.> change to `test` if only using within integration tests, for example.
+
+* add properties to enable logging.
+As a minimum, you could add just:
++
+[source,properties]
+.application.properties
+----
+# Register P6LogFactory to log JDBC events
+decorator.datasource.p6spy.enable-logging=true
+
+# Use com.p6spy.engine.spy.appender.MultiLineFormat instead of com.p6spy.engine.spy.appender.SingleLineFormat
+decorator.datasource.p6spy.multiline=true
+
+# Use logging for default listeners [slf4j, sysout, file, custom]
+decorator.datasource.p6spy.logging=sysout
+
+## Log file to use (only with logging=file)
+#decorator.datasource.p6spy.log-file=spy.log
+
+## Class file to use (only with logging=custom). The class must implement com.p6spy.engine.spy.appender.FormattedLogger
+#decorator.datasource.p6spy.custom-appender-class=my.custom.LoggerClass
+
+## Custom log format, if specified com.p6spy.engine.spy.appender.CustomLineFormat will be used with this log format
+#decorator.datasource.p6spy.log-format=
+
+## Use regex pattern to filter log messages. If specified only matched messages will be logged.
+#decorator.datasource.p6spy.log-filter.pattern=
+
+## Report the effective sql string (with '?' replaced with real values) to tracing systems.
+## NOTE this setting does not affect the logging message.
+#decorator.datasource.p6spy.tracing.include-parameter-values=true
+----
+
+The link:https://github.com/gavlyukovskiy/spring-boot-data-source-decorator[integration] makes it easy to set the most common configuration properties, but it's also possible to configure other p6spy properties using its various configuration mechanisms; see https://p6spy.readthedocs.io/en/latest/configandusage.html[p6spy docs] for details.
+
+Rather than adding the properties directly to `application.properties`, you might prefer to set up a "p6spy" profile so that the configuration can be enabled/disabled easily:
+
+* create a properties file for a "p6spy" profile, in either the root or `config` package:
++
+[source,properties]
+.application-p6spy.properties
+----
+...
+----
+* move the configuration properties from `application.properties` into `application-p6spy.properties`
+
+* enable the `p6spy` profile using the `spring.profiles.active` system property:
++
+[source,bash]
+----
+-Dspring.profiles.active=p6spy
+----
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/setup-and-configuration.adoc b/persistence/jdo/adoc/modules/ROOT/pages/setup-and-configuration.adoc
index 56c0be9b25..074d9b545a 100644
--- a/persistence/jdo/adoc/modules/ROOT/pages/setup-and-configuration.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/pages/setup-and-configuration.adoc
@@ -94,18 +94,18 @@ The default value is to use SQL-99 syntax ("CREATE SCHEMA IF NOT EXISTS %S"), pa
 == Auto-create (or validate) tables
 
 When running against the h2 in-memory database (eg for prototype mode or integration tests), you'll probably want DataNucleus to automatically create the tables.
+This can be done either eagerly or lazily:
 
-This can be configured using the:
+* to create all tables eagerly, set xref:refguide:config:sections/datanucleus.adoc#datanucleus.schema.generate-database.mode[datanucleus.schema.generate-database.mode] to `create`.
 
-* xref:refguide:config:sections/datanucleus.adoc#datanucleus.schema.auto-create-all[datanucleus.schema.autoCreateAll] configuration property.
+* to create all tables lazily (only as objects are read from/written to them), set the xref:refguide:config:sections/datanucleus.adoc#datanucleus.schema.auto-create-all[datanucleus.schema.auto-create-all] to `true`.
 
-Conversely, if running in production then you will probably use xref:userguide:flyway:about.adoc[Flyway] to manage database migrations.
-In this case you'll just want to validate that the database table structure of the target database is in line with what DataNucleus expectss.
-
-You can do this using the:
+Generally we recommend eager creation; there are certain edge cases  -- eg inheritance hierarchies using the superclass (roll-up) table mappging -- where lazy creation can fail.
 
-* xref:refguide:config:sections/datanucleus.adoc#datanucleus.schema.validate-all[datanucleus.schema.validateAll] configuration property.
+Conversely, if running in production then you will probably use xref:userguide:flyway:about.adoc[Flyway] to manage database migrations.
+In this case you'll just want to validate that the database table structure of the target database is in line with what DataNucleus expects.
 
+You can do this using the xref:refguide:config:sections/datanucleus.adoc#datanucleus.schema.validate-all[datanucleus.schema.validateAll] configuration property.
 This will fail-fast if there is a mismatch.
 
 
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/java8.adoc b/persistence/jdo/adoc/modules/ROOT/pages/spis.adoc
similarity index 52%
rename from persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/java8.adoc
rename to persistence/jdo/adoc/modules/ROOT/pages/spis.adoc
index fcc6cfc4ef..cbfd08298e 100644
--- a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/java8.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/pages/spis.adoc
@@ -1,29 +1,8 @@
-[[java8]]
-= Java8
+= SPIs (hooks)
 
 :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: v2: should probably just remove this section.
-
-DataNucleus 4.x supports Java 7, but can also be used with Java 8, eg for streams support against collections managed
-by DataNucleus.
-
-Just include within `<dependencies>` of your `dom` module's `pom.xml`:
-
-[source,xml]
-----
-<dependency>
-    <groupId>org.datanucleus</groupId>
-    <artifactId>datanucleus-java8</artifactId>
-    <version>4.2.0-release</version>t
-</dependency>
-----
-
-[NOTE]
-====
-The DataNucleus website includes a link:http://www.datanucleus.org/products/accessplatform/compatibility.html[page]
-listing version compatibility of these extensions vis-a-vis the core DataNucleus platform.
-====
-
+This page lists (some of) the hooks available to interact with the JDO persistence mechanism itself.
 
+include::spis/JdoEntityDiscoveryListener.adoc[leveloffset=+1]
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/spis/JdoEntityDiscoveryListener.adoc b/persistence/jdo/adoc/modules/ROOT/pages/spis/JdoEntityDiscoveryListener.adoc
new file mode 100644
index 0000000000..8f25809350
--- /dev/null
+++ b/persistence/jdo/adoc/modules/ROOT/pages/spis/JdoEntityDiscoveryListener.adoc
@@ -0,0 +1,69 @@
+= JdoEntityDiscoveryListener
+
+: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 [...]
+
+
+The `JdoEntityDiscoveryListener` interface is called during bootstrapping after the `PersistenceManagerFactory` has been created.
+It can be used to perform additional bootstrapping tasks.
+
+For example, to eagerly register all named queries, you could use:
+
+[source,java]
+.Customer.java
+----
+@Component
+public class NamedQueryEagerRegistrar implements JdoEntityDiscoveryListener {
+
+    @Override
+    public void onEntitiesDiscovered(
+            final @NonNull PersistenceManagerFactory persistenceManagerFactory,
+            final @NonNull Set<Class<?>> entityTypes,
+            final @NonNull Map<String, Object> properties) {
+
+        val pmf = (JDOPersistenceManagerFactory) persistenceManagerFactory;
+        val nucleusContext = pmf.getNucleusContext();
+
+        val clr = nucleusContext.getClassLoaderResolver(_Context.getDefaultClassLoader());
+        val metaDataManager = nucleusContext.getMetaDataManager();
+
+        metaDataManager
+            .getClassesWithMetaData()
+            .forEach(className->{
+                val meta = metaDataManager.getMetaDataForClass(className, clr);
+                _NullSafe.stream(meta.getQueries())
+                .forEach(metaDataManager::registerNamedQuery);
+            });
+    }
+}
+----
+
+You could also use an implementation to eagerly create the database tables in the database:
+
+[source,java]
+.Customer.java
+----
+@Component
+public class DatabaseCreator implements JdoEntityDiscoveryListener {
+
+    @Override
+    public void onEntitiesDiscovered(
+            final @NonNull PersistenceManagerFactory persistenceManagerFactory,
+            final @NonNull Set<Class<?>> entityTypes,
+            final @NonNull Map<String, Object> properties) {
+
+        val pmf = (JDOPersistenceManagerFactory) persistenceManagerFactory;
+        val nucleusContext = pmf.getNucleusContext();
+        val schemaAwareStoreManager = (SchemaAwareStoreManager)nucleusContext.getStoreManager();
+
+        val classNames = entityTypes
+                .stream()
+                .map(Class::getName)
+                .collect(Collectors.toSet());
+
+        schemaAwareStoreManager.createSchemaForClasses(classNames, properties);
+    }
+}
+----
+
+However, for this second use case it's easier to simply set the `datanucleus.schema.generate-database.mode` configuration property.
+
diff --git a/persistence/jdo/adoc/modules/ROOT/partials/module-nav.adoc b/persistence/jdo/adoc/modules/ROOT/partials/module-nav.adoc
index db4c255ebb..596938d62b 100644
--- a/persistence/jdo/adoc/modules/ROOT/partials/module-nav.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/partials/module-nav.adoc
@@ -12,3 +12,4 @@
 
 * xref:pjdo:ROOT:dependency-injection.adoc[Dependency Injection]
 * xref:pjdo:ROOT:hints-and-tips.adoc[Hints-n-Tips]
+* xref:pjdo:ROOT:spis.adoc[SPIs (hooks)]
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
index 6eb284ef24..13c7d5e198 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/IsisModulePersistenceJdoDatanucleus.java
@@ -20,8 +20,8 @@ package org.apache.isis.persistence.jdo.datanucleus;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
-import javax.inject.Named;
 import javax.inject.Provider;
 import javax.jdo.JDOException;
 import javax.jdo.PersistenceManagerFactory;
@@ -48,7 +48,7 @@ import org.apache.isis.core.config.beans.aoppatch.TransactionInterceptorFactory;
 import org.apache.isis.core.metamodel.context.MetaModelContext;
 import org.apache.isis.core.transaction.changetracking.EntityChangeTracker;
 import org.apache.isis.persistence.jdo.datanucleus.changetracking.JdoLifecycleListener;
-import org.apache.isis.persistence.jdo.datanucleus.config.DnSettings;
+import org.apache.isis.persistence.jdo.datanucleus.config.DatanucleusSettings;
 import org.apache.isis.persistence.jdo.datanucleus.dialect.DnJdoDialect;
 import org.apache.isis.persistence.jdo.datanucleus.entities.DnEntityStateProvider;
 import org.apache.isis.persistence.jdo.datanucleus.jdosupport.JdoSupportServiceDefault;
@@ -86,7 +86,7 @@ import lombok.extern.log4j.Log4j2;
     JdoSupportServiceDefault.class,
 
 })
-@EnableConfigurationProperties(DnSettings.class)
+@EnableConfigurationProperties(DatanucleusSettings.class)
 @Log4j2
 public class IsisModulePersistenceJdoDatanucleus {
 
@@ -110,7 +110,7 @@ public class IsisModulePersistenceJdoDatanucleus {
             final EventBusService eventBusService,
             final Provider<EntityChangeTracker> entityChangeTrackerProvider,
             final IsisBeanTypeRegistry beanTypeRegistry,
-            final DnSettings dnSettings) {
+            final DatanucleusSettings dnSettings) {
 
         _Assert.assertNotNull(dataSource, "a datasource is required");
 
@@ -144,21 +144,18 @@ public class IsisModulePersistenceJdoDatanucleus {
             final @Qualifier("local-pmf-proxy") LocalPersistenceManagerFactoryBean localPmfBean,
             final IsisBeanTypeRegistry beanTypeRegistry,
             final List<JdoEntityDiscoveryListener> jdoEntityDiscoveryListeners,
-            final DnSettings dnSettings) {
+            final DatanucleusSettings dnSettings) {
 
         val pmf = localPmfBean.getObject(); // created once per application lifecycle
 
-        final Map<String, String> props = _Maps.newLinkedHashMap();
-        dnSettings.getDatanucleus().forEach((key, value) -> props.put("datanucleus." + key, value));
-        dnSettings.getJavax().entrySet().stream()
-                .filter(x -> x.getKey().startsWith("jdo."))
-                .forEach( x -> props.put("javax." + x.getKey(), x.getValue())
-        );
-
-        _NullSafe.stream(jdoEntityDiscoveryListeners)
-                .forEach(listener->{
-                    listener.onEntitiesDiscovered(pmf, beanTypeRegistry.getEntityTypes(), props);
-                });
+        val entityTypes = beanTypeRegistry.getEntityTypes();
+        if(! _NullSafe.isEmpty(entityTypes)) {
+            val dnProps = dnSettings.getAsProperties();
+            _NullSafe.stream(jdoEntityDiscoveryListeners)
+                    .forEach(listener->{
+                        listener.onEntitiesDiscovered(pmf, entityTypes, dnProps);
+                    });
+        }
 
         val tapmfProxy = new TransactionAwarePersistenceManagerFactoryProxy(metaModelContext);
         tapmfProxy.setTargetPersistenceManagerFactory(pmf);
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnSettings.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DatanucleusSettings.java
similarity index 65%
rename from persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnSettings.java
rename to persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DatanucleusSettings.java
index 2fa5a00f82..c75837b1cc 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DnSettings.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/config/DatanucleusSettings.java
@@ -20,6 +20,7 @@ package org.apache.isis.persistence.jdo.datanucleus.config;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 import javax.annotation.Priority;
@@ -35,20 +36,32 @@ import org.apache.isis.persistence.jdo.datanucleus.entities.DnObjectProviderForI
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 /**
+ * This class slurps up all configuration properties but exposes (through {@link #getAsProperties()} only those
+ * prefixed either "datanucleus." or "javax.jdo".  It also sanitizes the keys, converting from <code>kebab-case</code>
+ * into a form that DN is happy with.
+ *
+ * <p>
+ *     A bit more detail on the above: DN specifies that configuration parameters should be in <code>camelCase</code>,
+ *     however that is not the normal idiom for Spring Boot, moreover specifying camelCase properties, which possible,
+ *     is laborious.  Luckily DN actually lower cases all properties anyway.  It's therefore pretty simple for this
+ *     class to sanitize keys from kebab case to lower case.
+ * </p>
+ *
  * @since 2.0
  */
 @Configuration
 @Named("isis.persistence.jdo.DnSettings")
 @Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("Dn5")
+@Qualifier("DN6")
 @ConfigurationProperties(
         prefix = "",
         ignoreUnknownFields = true)
 @Log4j2
-public class DnSettings {
+public class DatanucleusSettings {
 
     /** mapped by {@code datanucleus.*} */
     @Getter @Setter
@@ -61,13 +74,17 @@ public class DnSettings {
     private final Object lock = new Object();
     private Map<String, Object> properties;
 
+    /**
+     * Returns all "datanucleus.*" and "javax.jdo" configuration properties, with the keys converted to lowercase
+     * (from kebab case) so that they are recognised by DataNucleus.
+     */
     public Map<String, Object> getAsProperties() {
         synchronized(lock) {
             if(properties==null) {
                 properties = new HashMap<>();
 
                 if(datanucleus!=null) {
-                    datanucleus.forEach((k, v)->properties.put("datanucleus." + k, v));
+                    datanucleus.forEach((k, v)->properties.put(sanitizeKey("datanucleus." + k), v));
                 }
 
                 if(javax!=null) {
@@ -76,20 +93,18 @@ public class DnSettings {
                     .forEach(e->properties.put("javax." + e.getKey(), e.getValue()));
                 }
 
-                amendProperties(properties);
+                addFallbacks(properties);
             }
         }
         return properties;
     }
 
-    private void amendProperties(final Map<String, Object> props) {
+    private void addFallbacks(final Map<String, Object> props) {
 
-        // add optional defaults if needed
-
-        String connectionFactoryName = (String) props.get(PropertyNames.PROPERTY_CONNECTION_FACTORY_NAME);
+        val connectionFactoryName = (String) props.get(PropertyNames.PROPERTY_CONNECTION_FACTORY_NAME);
         if(connectionFactoryName != null) {
-            String connectionFactory2Name = (String) props.get(PropertyNames.PROPERTY_CONNECTION_FACTORY2_NAME);
-            String transactionType = (String) props.get("javax.jdo.option.TransactionType");
+            val connectionFactory2Name = (String) props.get(PropertyNames.PROPERTY_CONNECTION_FACTORY2_NAME);
+            val transactionType = (String) props.get("javax.jdo.option.TransactionType".toLowerCase());
             // extended logging
             if(transactionType == null) {
                 log.info("found config properties to use non-JTA JNDI datasource ({})", connectionFactoryName);
@@ -105,13 +120,23 @@ public class DnSettings {
             } else {
                 log.info("... and config properties for second '-nontx' JNDI datasource also found; {}", connectionFactory2Name);
             }
-            // nothing further to do
         }
 
         props.computeIfAbsent(PropertyNames.PROPERTY_STATE_MANAGER_CLASS_NAME,
                 key->DnObjectProviderForIsis.class.getName());
 
+        // we debated whether to default 'create' mode, ie eagerly create the database tables ... however while this is
+        // fine for integration testing, it doesn't make much sense for production usage.  So instead we'll just make
+        // sure it is well documented, and in the sample apps.
+        //
+        // props.computeIfAbsent(PropertyNames.PROPERTY_SCHEMA_GENERATE_DATABASE_MODE, s -> "create");
+
     }
 
+    private static String sanitizeKey(String key) {
+        return key.replaceAll("-", "").toLowerCase();
+    }
+
+
 
-}
\ No newline at end of file
+}
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/jdosupport/JdoSupportServiceDefault.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/jdosupport/JdoSupportServiceDefault.java
index b4149155a5..a3202ca1d7 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/jdosupport/JdoSupportServiceDefault.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/jdosupport/JdoSupportServiceDefault.java
@@ -57,7 +57,7 @@ import lombok.val;
 @Service
 @Named("isis.persistence.jdo.JdoSupportServiceDefault")
 @Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("DN5")
+@Qualifier("DN6")
 public class JdoSupportServiceDefault implements JdoSupportService {
 
     @Inject private TransactionAwarePersistenceManagerFactoryProxy pmf;
diff --git a/persistence/jdo/provider/src/main/java/org/apache/isis/persistence/jdo/provider/config/JdoEntityDiscoveryListener.java b/persistence/jdo/provider/src/main/java/org/apache/isis/persistence/jdo/provider/config/JdoEntityDiscoveryListener.java
index 632aa889c8..a6b5e59d6a 100644
--- a/persistence/jdo/provider/src/main/java/org/apache/isis/persistence/jdo/provider/config/JdoEntityDiscoveryListener.java
+++ b/persistence/jdo/provider/src/main/java/org/apache/isis/persistence/jdo/provider/config/JdoEntityDiscoveryListener.java
@@ -23,6 +23,10 @@ import java.util.Set;
 
 import javax.jdo.PersistenceManagerFactory;
 
+import org.apache.isis.core.config.DatanucleusConfiguration;
+
+import lombok.NonNull;
+
 /**
  * Any implementations will be called during bootstrapping, after the {@link PersistenceManagerFactory} has been
  * created.
@@ -30,7 +34,7 @@ import javax.jdo.PersistenceManagerFactory;
  * <p>
  *     This is a good way to perform any eager initialization.  However, for the specific use case of creating the
  *     database tables (eg if integration testing) a better approach is to simply set the
- *     {@link org.apache.isis.core.config.DatanucleusConfiguration.Schema.Generatedatabase#setMode(String) datanucleus.schema.generatedatabase.mode}
+ *     {@link DatanucleusConfiguration.Schema.GenerateDatabase#setMode(String) datanucleus.schema.generate-database.mode}
  *     configuration property; there's no need to implement a listener for this use case.
  * </p>
  */
@@ -43,7 +47,7 @@ public interface JdoEntityDiscoveryListener {
      */
     public void onEntitiesDiscovered(
             PersistenceManagerFactory persistenceManagerFactory,
-            Set<Class<?>> entityTypes,
-            Map<String, String> properties);
+            @NonNull Set<Class<?>> entityTypes,
+            @NonNull Map<String, Object> properties);
 
 }