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.