From 5d0162381aebeee02575ba6cf87fb897e8a2e170 Mon Sep 17 00:00:00 2001 From: Richard Zowalla Date: Wed, 2 Sep 2026 20:03:12 +0200 Subject: [PATCH] [OPENJPA-2992] Log the classes skipped for want of metadata A class listed in a persistence unit that resolves to no metadata is skipped rather than failing entity manager creation. That part is right and stays: Jakarta Persistence 3.2 chapter 8 says nothing about a listed class that is not a managed type, so the behaviour is provider defined, Hibernate and EclipseLink both skip, and the Jakarta Persistence TCK requires it, since its own persistence units list plain classes such as LineItemException alongside entities. No single TCK test demands it. What was wrong is that the skip was silent, so a forgotten @Entity annotation or a missing orm.xml entry went unreported. Building the metamodel skipped without a word, and schema synchronization warned with a hardcoded English string. Both now log a message naming the class and what to check, and the reasoning is recorded next to the code rather than left to the commit log. The migration considerations are corrected as well: they claimed a warning on a log that does not carry one, and did not mention that with the default openjpa.RuntimeUnenhancedClasses=unsupported such a class is still rejected at start-up by the runtime enhancer. --- .../jdbc/kernel/JDBCBrokerFactory.java | 4 ++-- .../openjpa/jdbc/kernel/localizer.properties | 4 ++++ .../enhance/ManagedClassSubclasser.java | 18 +++++++-------- .../simple/TestMissingMetaData.java | 22 ++++++++++++++----- .../persistence/meta/MetamodelImpl.java | 19 +++++++++++++--- .../persistence/meta/localizer.properties | 3 +++ .../doc/manual/migration_considerations.xml | 17 +++++++++----- 7 files changed, 62 insertions(+), 25 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java index 974f32686a..0689ae977f 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCBrokerFactory.java @@ -244,9 +244,9 @@ protected boolean synchronizeMappings(ClassLoader loader, JDBCConfiguration conf if (tool.getRepository().getMetaData(cls, null, false) != null) { throw mde; } + // see MetamodelImpl for why such a class is skipped conf.getLog("openjpa.jdbc.Schema").warn( - "Skipping schema synchronization for non-managed class: " - + cls.getName()); + _loc.get("skip-unmanaged-class", cls.getName())); } } tool.record(); diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties index 382a7564f9..79ff598255 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/kernel/localizer.properties @@ -183,3 +183,7 @@ alter-seq-disabled: The property "openjpa.jdbc.DBDictionary=disableAlterSeqenceI which is defined in the entity''s sequence. With this SQL statement disabled, it is the \ responsibility of the user to ensure that the entity''s sequence definition matches the \ sequence defined in the database. +skip-unmanaged-class: The class "{0}" is listed in the persistence unit but \ + has no persistence metadata, and is skipped during schema \ + synchronization. If it was meant to be managed, check that it is \ + annotated or mapped in orm.xml. diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java index 2bcba74931..feb93b9c9a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java @@ -45,7 +45,6 @@ import org.apache.openjpa.util.GeneratedClasses; import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.InternalException; -import org.apache.openjpa.util.MetaDataException; import org.apache.openjpa.util.UserException; import org.apache.openjpa.util.asm.ClassNodeTracker; @@ -153,17 +152,16 @@ public void write(ClassNodeTracker cnt) throws IOException { // set this before enhancement as well as after since enhancement // uses a different metadata repository, and the metadata config - // matters in the enhancement contract. In order to avoid a - // NullPointerException, check for no metadata and throw an - // exception if none exists. Otherwise, don't do any warning here, - // since we'll issue warnings when we do the final metadata - // reconfiguration at the end of this method. + // matters in the enhancement contract. ClassMetaData meta = enhancer.getMetaData(); if (meta == null) { - // non-entity classes (DTOs, listeners, ID classes) may be - // listed in persistence.xml elements; skip them - if (log.isWarnEnabled()) - log.warn(_loc.get("no-meta", cls)); + // a persistence unit may legitimately list a class that is + // not a managed type; skip it rather than failing, and log + // it so that a forgotten annotation stays diagnosable. + // See MetamodelImpl for the reasoning. + if (log.isWarnEnabled()) { + log.warn(_loc.get("no-meta", cls.getName())); + } continue; } configureMetaData(meta, conf, redefine, false); diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestMissingMetaData.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestMissingMetaData.java index ed966159cc..be8a53b4ba 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestMissingMetaData.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestMissingMetaData.java @@ -46,16 +46,28 @@ public void setUp() { } /** - * Verify that a non-entity class listed in persistence.xml is - * gracefully skipped. Per JPA 3.2, persistence units may list - * non-entity managed classes (e.g. converters, listeners, - * ID classes, exceptions) which should not cause errors. + * Verify that a class listed in persistence.xml without persistence + * metadata is skipped rather than failing entity manager creation. + *

+ * Jakarta Persistence 3.2 chapter 8 does not say what a provider must do + * with such a class, so the behaviour is provider defined; Hibernate and + * EclipseLink both skip, and the Jakarta Persistence TCK requires it, + * since its own persistence units list plain classes alongside entities. + * The skip is logged, so a forgotten annotation stays diagnosable; see + * MetamodelImpl and JDBCBrokerFactory. */ public void testMissingMetaData() { - // Should not throw — non-entity classes are silently skipped emf.createEntityManager().close(); } + /** + * The metamodel is built over the same class list, and must skip the + * unmanaged class rather than fail. + */ + public void testMissingMetaDataInMetamodel() { + assertNotNull(emf.getMetamodel()); + } + @Override public void tearDown() { emf.close(); diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/MetamodelImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/MetamodelImpl.java index ff587d0969..7ffbb8e740 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/MetamodelImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/MetamodelImpl.java @@ -50,6 +50,7 @@ import org.apache.openjpa.kernel.exps.FilterListener; import org.apache.openjpa.kernel.exps.Resolver; import org.apache.openjpa.lib.util.J2DoPrivHelper; +import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.FieldMetaData; @@ -89,9 +90,21 @@ public MetamodelImpl(MetaDataRepository repos) { ClassMetaData meta = repos.getMetaData(cls, null, false); if (meta == null) { - // Skip non-entity classes (e.g. exceptions, ID classes, - // listeners) that are listed in persistence.xml - // elements but have no JPA metadata. + // OPENJPA-2940 / OPENJPA-2992: a class listed in a + // element that resolves to no metadata is skipped rather + // than failing. Jakarta Persistence 3.2 chapter 8 says + // nothing about a listed class that is not an entity, + // embeddable, mapped superclass or converter, so the + // behaviour is provider defined, and both Hibernate and + // EclipseLink skip. The Jakarta Persistence TCK requires + // tolerating it: its persistence units list plain classes + // such as LineItemException alongside real entities. + // The skip is logged so that a forgotten @Entity annotation + // or a missing orm.xml entry stays diagnosable. + Log log = repos.getLog(); + if (log.isWarnEnabled()) { + log.warn(_loc.get("skip-unmanaged-class", cls.getName())); + } continue; } PersistenceType type = getPersistenceType(meta); diff --git a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties index 405ec22eb9..f959b00023 100644 --- a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties +++ b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties @@ -94,3 +94,6 @@ decl-version-not-found: Declared version attribute of {1} type not found in {2} version-not-found: Version attribute of {1} type not found in {2} generic-type-param: Can not determine generic type parameter for field {0} of type {1} in {2}. \ Using java.lang.Object. +skip-unmanaged-class: The class "{0}" is listed in the persistence unit but \ + has no persistence metadata, and is left out of the metamodel. If it was \ + meant to be managed, check that it is annotated or mapped in orm.xml. diff --git a/openjpa-project/src/doc/manual/migration_considerations.xml b/openjpa-project/src/doc/manual/migration_considerations.xml index ec66dcacb4..035904acf3 100644 --- a/openjpa-project/src/doc/manual/migration_considerations.xml +++ b/openjpa-project/src/doc/manual/migration_considerations.xml @@ -718,11 +718,18 @@ Listing a class without persistence metadata in a persistence unit (class element) previously failed at start-up with "No registered metadata for type", in the runtime enhancer, in - getMetamodel() and during schema synchronization. Such classes - are now skipped with a warning on the openjpa.Enhance and - openjpa.jdbc.Schema logs. A forgotten @Entity - annotation is therefore no longer detected at start-up; watch the logs for the new - warnings. + getMetamodel() and during schema synchronization. Such a class + is now skipped instead, because a persistence unit may legitimately list + classes that are not managed types. The skip is logged as a warning on the + openjpa.MetaData log when the metamodel is built and on the + openjpa.jdbc.Schema log during schema synchronization, so a + forgotten @Entity annotation remains diagnosable. + + + Note that with the default openjpa.RuntimeUnenhancedClasses=unsupported + such a class is still rejected at start-up, by the runtime enhancer, with + "This configuration disallows runtime optimization"; the skip described above + applies once that check passes.