1

I have a project where we use Hibernate-jpa-2.1 and get an EntityManager istance both in this way

@PersistenceContext(unitName = "common-api")
EntityManager em;

and this

    em = Persistence.createEntityManagerFactory("common-api").createEntityManager();

because some classes aren't EJB and others are (I guess). The problem is that the createEntityManagerFactory fails with the error:

Caused by: javax.persistence.PersistenceException: Invalid persistence.xml.
Error parsing XML [line : -1, column : -1] : cvc-complex-type.2.4.a: Invalid content was found starting with element 'class'. One of '{"http://xmlns.jcp.org/xml/ns/persistence":validation-mode, "http://xmlns.jcp.org/xml/ns/persistence":properties}' is expected.

        at org.hibernate.jpa.boot.internal.PersistenceXmlParser.validate(PersistenceXmlParser.java:357)
        at org.hibernate.jpa.boot.internal.PersistenceXmlParser.loadUrl(PersistenceXmlParser.java:290)
        at org.hibernate.jpa.boot.internal.PersistenceXmlParser.parsePersistenceXml(PersistenceXmlParser.java:94)
        at org.hibernate.jpa.boot.internal.PersistenceXmlParser.doResolve(PersistenceXmlParser.java:84)
        at org.hibernate.jpa.boot.internal.PersistenceXmlParser.locatePersistenceUnits(PersistenceXmlParser.java:66)
        at org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilderOrNull(HibernatePersistenceProvider.java:80)
        ... 80 more

This is the persistence.xml:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="common-api" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>(...)</jta-data-source>
        <class>(the only entity we have in this Maven module)</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
        <validation-mode>NONE</validation-mode>
        <properties>
            <property name="javax.persistence.validation.mode" value="NONE" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL57InnoDBDialect" />
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.order_updates" value="true" />
            <property name="hibernate.max_fetch_depth" value="2" />
            <property name="hibernate.use_sql_comments" value="true" />
            <property name="hibernate.use_identifer_rollback" value="true" />
            <property name="hibernate.jdbc.batch_size" value="0" />
            <property name="hibernate.jdbc.batch_versioned_data" value="false" />
            <property name="hibernate.jdbc.use_get_generated_keys" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="none"/>
        </properties>
    </persistence-unit>
</persistence>

I don't understand why this happens because my classes are in the same package, same maven module, use the same Hibernate entities and the same persistence.xml.

N.B.: Making this class an EJB isn't an option.

1 Answers1

2

This is an error you should only get if the element order is wrong (and not the same as you posted here). The order of elements in persistence.xml is important during validation. It's described in the xsd. In each persistence-unit it is as follows:

  1. description
  2. provider
  3. jta-data-source
  4. non-jta-data-source
  5. mapping-file
  6. jar-file
  7. class
  8. exclude-unlisted-classes
  9. shared-cache-mode
  10. validation-mode
  11. properties
coladict
  • 4,799
  • 1
  • 16
  • 27
  • Hi! The problem is exactly that one: it works with the annotation, but not with the constructor. And yes, I double checked again and the elements are in the correct order. Is it possible that another file outside that folder is taken (for some reason)? How can I properly debug the issue to provide more information? – Capitano Giovarco Oct 27 '20 at 08:26
  • I mean the factory, not the constructor. – Capitano Giovarco Oct 27 '20 at 09:08
  • You can check for that by way of `java.net.URLClassLoader`, which every non-bootstrap classloader extends. `URLClassLoader ucl = (URLClassLoader) Persistence.class.getClassLoader()` then `ucl.findResources("META-INF/persistence.xml")` and check to see where it's reading it from. – coladict Oct 27 '20 at 09:14
  • Although maybe `Persistence` is not the best class to get it from, in case it's different. Maybe try with `org.hibernate.jpa.HibernatePersistenceProvider` just to be sure. – coladict Oct 27 '20 at 09:20
  • 1) I should run the class loader thing right before where I'm trying to create the entityManager, right? 2) How do you propose to use `HibernatePersistenceProvider`? My actual code looks like this `em.createNativeQuery("SELECT * FROM oauth2_access a", OAuth2Access.class).getResultList()` I'll also try yo lower the logging level of hibernate to DEBUG on `org.hibernate` to see if I can find any useful information – Capitano Giovarco Oct 27 '20 at 10:03
  • When I try to get the classLoader, I get this error: `java.lang.ClassCastException: org.jboss.modules.ModuleClassLoader cannot be cast to java.net.URLClassLoader` – Capitano Giovarco Oct 27 '20 at 10:23
  • Setting the logging level to DEBUG didn't give me any hint about where the problem is – Capitano Giovarco Oct 27 '20 at 10:45
  • Well that's new. Never encountered a third-party one that doesn't just extend URLClassLoader. However the API for this isn't really needed from `URLClassLoader`. That's just my habit of listing the full search path. You can use `ClassLoader.getResources(String name)` which is a public method – coladict Oct 27 '20 at 10:45
  • Unfortunately the method `getResources` isn't static for me. An instance is needed. I use Java 8 – Capitano Giovarco Oct 27 '20 at 11:04
  • Sorry, I didn't think I'd have to mention to get the ClassLoader from your chosen class, and then call `getResources` on it. – coladict Oct 27 '20 at 11:06
  • Thanks for specifying! I deployed some logs to debug and there are two persistence.xml files in two different jars in our deployments folder, one jar is the Maven project I'm changing and the other one is another Maven project (beloging to the same product). Worth noting that the first XML in the Enumeration is the wrong XML, so I suspect that the wrong persistence.xml file is taken (for some reason). Is there any way to enforce the usage of the second XML? – Capitano Giovarco Oct 27 '20 at 11:42
  • You can change the name of the persistence unit, but other than that, it depends on whatever system is loading them. I haven't worked with SpringBoot yet, but I'm guessing that's what you're using. – coladict Oct 27 '20 at 11:51
  • Mine isn't a Spring Boot project but a Wildfly 11 one. The thing is that the name of the persistence unit names are different and transaction-type is RESOURCE_LOCAL there, while in my project is JTA. I don't understand how my class has "scope" over another jar, furthermore I have a class that uses the EntityManagerFactory in the same package but doesn't have this problem. Any idea? – Capitano Giovarco Oct 27 '20 at 13:04
  • For reference, I tried to change my transaction-type from JTA to RESOURCE_LOCAL and I get a list of 4 possible persistence.xml files. The one I want is always in the bottom of the list... – Capitano Giovarco Oct 27 '20 at 13:16