no persistence provider for EntityManager named pe

2019-02-21 08:08发布

问题:

This is not a duplicate as the tag above purports. This question has not yet been answered.

I am trying to set up this tutorial to get hyperjaxb to work in an eclipse project. How can I get it to see a persistence provider? hbm2ddl has NOT created the table structure in the database yet. Is that why the app is not seeing a persistence provider? Or is it because the contents of persistence.xml in target/generated-sources are not accessible from the src/main/java directory in which my TestFunctions.java is trying to call it? Here are the specifics... This line of code:

entityManagerFactory = Persistence.createEntityManagerFactory("persistence.xml", persistenceProperties);  

Is throwing this error:

Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named persistence.xml
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
    at maintest.TestFunctions.setUpPersistence(TestFunctions.java:119)
    at maintest.Main.main(Main.java:10)

The code throwing the error is in this method:

public void setUpPersistence(){
    final Properties persistenceProperties = new Properties();
    InputStream is = null;
    try {
        Class<? extends TestFunctions> c = getClass();
        ClassLoader cl = c.getClassLoader();
        is = cl.getResourceAsStream("persistence.properties");
        persistenceProperties.load(is);
    }catch (IOException i) {i.printStackTrace();}
    finally {if (is != null) {try {is.close();} catch (IOException ignored) {}}}
    entityManagerFactory = Persistence.createEntityManagerFactory("persistence.xml", persistenceProperties);
}  

persistence.xml:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="1.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <persistence-unit name="org.jvnet.hyperjaxb3.ejb.tests.po">
            <class>org.jvnet.hyperjaxb3.ejb.tests.po.Items</class>
            <class>org.jvnet.hyperjaxb3.ejb.tests.po.Items$Item</class>
            <class>org.jvnet.hyperjaxb3.ejb.tests.po.PurchaseOrderType</class>
            <class>org.jvnet.hyperjaxb3.ejb.tests.po.USAddress</class>
        </persistence-unit>
    </persistence>

persistence.properties is:

hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.username=someusername
hibernate.connection.password=somepassword
hibernate.connection.url=jdbc:mysql://localhost/somedatabasename
hibernate.hbm2ddl.auto=create-drop
hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
hibernate.jdbc.batch_size=0

The directory structure for the eclipse project is:


**EDIT: **

Just for kicks, I moved the META-INF folder to the same level as the calling class, but I get the same error. Here are two new screen shots showing locations of persistence.xml in which the error is still thrown:


SECOND EDIT:

I added Class c = Class.forName("org.eclipse.persistence.jpa.PersistenceProvider"); at line 116 of TestFunctions.java, which triggers the following error when I right click on Main.java and do Run As..Java Application:

Exception in thread "main" java.lang.ClassNotFoundException: org.eclipse.persistence.jpa.PersistenceProvider
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)
    at maintest.TestFunctions.setUpPersistence(TestFunctions.java:116)
    at maintest.Main.main(Main.java:10)  

Here is the new TestFunctions.setUpPersistence() method:

public void setUpPersistence() throws ClassNotFoundException{
    final Properties persistenceProperties = new Properties();
    InputStream is = null;
    try {
        Class<? extends TestFunctions> c = getClass();
        ClassLoader cl = c.getClassLoader();
        is = cl.getResourceAsStream("persistence.properties");
        persistenceProperties.load(is);
    }catch (IOException i) {i.printStackTrace();}
    finally {if (is != null) {try {is.close();} catch (IOException ignored) {}}}//org.jvnet.hyperjaxb3.ejb.tests.po
    Class c = Class.forName("org.eclipse.persistence.jpa.PersistenceProvider");
    entityManagerFactory = Persistence.createEntityManagerFactory(/*"org.jvnet.hyperjaxb3.ejb.tests.po"*//*"persistence.xml"*/"org.jvnet.hyperjaxb3.ejb.tests.po", persistenceProperties);
}

When I replace Class c = Class.forName("org.eclipse.persistence.jpa.PersistenceProvider"); with Class c = Class.forName("org.hibernate.ejb.HibernatePersistence");, I get a similar error. However, Class c = Class.forName("javax.persistence.spi.PersistenceProvider"); does not throw any error, so the program continues until the same javax.persistence.PersistenceException: No Persistence provider for EntityManager named org.jvnet.hyperjaxb3.ejb.tests.po error gets thrown further downstream.

Does this tell us more about the cause of the error? Also, hyperjaxb creates a persistence.xml and places it in the target folder. This error occurred when persistence.xml was in that location, and has persisted when I move persistence.xml as shown above. Is hyperjaxb causing this problem by not playing nicely with eclipse?

回答1:

I just can't resist, this is my final attempt to help. :) I'm writing this for you and for any users who may encounter similar issues.

I believe you simply have a problem in the Eclipse project configuration. The paths in your Eclipse project do not seem correct.

Normally, when you import an existing Maven project into Eclipse using File > Import... > Existing Maven Projects, m2eclipse should analyse the structure of the Maven project and create Eclipse project with (.classpath and .project files) automatically.

The paths in these projects are derived from your Eclipse pom.xml. So you actually should not create/modify/add/remove/copy/move/whatever Eclipse source folders. If you do this, your Eclipse project will no longer be synchronized with your Maven project and you'll get really weird results.

Sometimes m2eclipse does not do its job well. Sometimes you don't get the right source folders, things go missing etc. From my experience, this got radically better over the time and right now m2eclipse works like a charm. From what I saw in your struggles, it might be that you have an older version of m2eclipse. Maybe not, but I'd suggest to check it anyway.

Next, sometimes m2eclispe still clamps. In this cases it is highly recommended to do the following things:

  • Clean the project: Project > Clean...
  • Update the Maven project (Alt + F5). Make sure to update the project configuration from pom.xml

Finally you should get the following project structure:

Specifically, you should get the following source folders:

  • src\main\java - source folder for your main Java files
  • src\main\resources - source folder for resources
  • target\generated-sources\xjc - source folder for the generated code.
    • Important: not target\generated-sources like you have but target\generated-sources\xjc.
    • This folder contains the generated Java classes. (Hyperjaxb adds JPA annotations to them).
    • It also contains META-INF\persistence.xml which is a persistence unit descriptor. The META-INF\persistence.xml resource is specified by the JPA spec. This is where the persistence unit will be loaded from.
    • If you've generated a roundtrip test (xml-to-the-db-and-back), then you'll also see a RoundtripTest class somewhere. This is a JUnit test class, it should be possible to execute it directly. (Run As > JUnit Test)
  • src\test\java - test classes.
  • src\test\resources - test resources

This is a standard directory layout for Maven projects. The only peculiarity is the target\generated-sources\xjc source folder which holds the code generated by the schema compiler. And even this is absolutely standard, it is a usual convention for Maven code-generation plugins to generate code in target\generated-sources\some-tool directories.

Below I post the .classpath file from my project:

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" output="target/classes" path="src/main/java">
        <attributes>
            <attribute name="optional" value="true"/>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry including="**/*.java" kind="src" output="target/classes" path="target/generated-sources/xjc">
        <attributes>
            <attribute name="optional" value="true"/>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
        <attributes>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="src" output="target/test-classes" path="src/test/java">
        <attributes>
            <attribute name="optional" value="true"/>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
        <attributes>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
        <attributes>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
        <attributes>
            <attribute name="maven.pomderived" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="output" path="target/classes"/>
</classpath>

Important

If you don't have this directory structure (and your screenshots show that you don't) DO NOT move on. It will fail (it does for you). Fix your project structure first. Do not move on, don't try to run anything, you'll run into all kinds of strange things like not found resources etc. (which you do).

Regenerate project structure from pom.xml and clean the project in Eclipse. Open your .classpath file and compare it line-by-line with what I posted. But please do fix the project structure first.

Next, it is important that you understand that you are not supposed to move or drag-and-drop the generated code. It is not the right thing to do. Don't do it. Please don't move on until you accept this as an imperative. Don't move or modify the generated code. If you do, you overtake the responsibility for this code. Things are generated the way they are because it works this way. So if you change it it is very likely that it won't work anymore (it does not for you). If you will need to change anything, you change the pom.xml which configures the generation, not the resulting code.

Now, let us analyse your current project structure based on the following screenshot:

What I can notice:

  • src\main\java contains maintest\Main and TestFunctions. If you're trying to write a test, put your test classes into src\test\java, not src\main\java.
  • src\main\resources contains persistence.xml, sun-jaxb.episode, persistence.properties and schema.xsd. Only schema.xsd should be there.
    • persistence.xml and sun-jaxb.episode are generated files. And, see above, you must not move them.
    • persistence.properties defines the connection to the database used for testing. So it must be in test resources, namely src\test\resources. Putting it into src\main\resources will finally add this file into the resulting JAR, it's like telling everyone username and password of the database you use for testing
  • src\test\java - this is OK, but your test classes should be here.
  • src\test\resources - persistence.properties should be here.
  • target\generated-sources - this is wrong, this should be target\generated-sources\xjc
    • org.jvnet.hyperjaxb3.ejb.test.po package is under target\generated-sources. It is generated under target\generated-sources\xjc, so what I see means you've moved. Don't do this, see above.
    • xjc is displayed as package. This is actually a leftover after you've (incorrectly) configured target\generated-sources as a source folder. This folder must contain all of the generated sources, now it only contains the RoundtripTest. Which means you moved the generated code. Don't do this, see the imperative above.

So your Eclipse project configuration as displayed above is invalid. Here's my theory on why this happened. I think that your m2eclipse did not process the pom.xml correctly, you were probably missing folders etc. But instead of getting the Eclipse project files to be correctly generated my m2eclipse you have manually added the target\generated-sources and started moving the generated code around. This is not the right way.

Please check and re-check your .classpath until it is equivalent to what I posted. I have zipped and uploaded my sample project which definitely works for me. You can unzip and import it using File > Import... > Existing Projects into Eclipse (not as Maven project).

Finally, you must be able to execute the RoundtripTest as a JUnit test. The test will probably fail, but it must run, you should not be getting NPEs etc. I don't have a MySQL database at hand, so this is what I get at the moment:

Detected [file:/C:/Projects/workspaces/hj3/hyperjaxb3-ejb-template-basic-0.6.0/target/classes/META-INF/persistence.xml].
    RoundtripTest

Loading entity manager factory properties.
    RoundtripTest

Loading entity manager factory properties from [file:/C:/Projects/workspaces/hj3/hyperjaxb3-ejb-template-basic-0.6.0/target/test-classes/persistence.properties].
    RoundtripTest

Could not obtain connection to query metadata
    org.hibernate.cfg.SettingsFactory

com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception: 

** BEGIN NESTED EXCEPTION ** 

java.net.ConnectException
MESSAGE: Connection refused: connect

This means that entity manager started correctly but could not connect to the database. If you've configured your test database in the src\test\resources\persistence.properties corretcly, you should be able to connect to the database. And if you'll put a po.xml file to src\test\samples, the roundtrip test will:

  • unmarshal this files
  • save it into the database
  • load it back
  • compare it to the original result

Please get the roundrip test run first before you start your own experiments. Don't copy the code, don't modify it, don't try to bend it to your directory structure. It must run as it is. Don't move on until it does.

Good luck.

Update.

You might also be facing an issue with unresolved dependencies. Please make sure your dependencies are resolved correctly. This is what I see:

If you don't see this, it is bad. Something is wrong with your workspace. Check if you have any error messages in Eclipse. Maybe you have something like:

Plugin execution not covered by lifecycle configuration: org.codehaus.mojo:hibernate3-maven-plugin

Quick-fix it first ("Permanently mark goal ... as ignored in Eclispe build"). Don't move on until this error is gone. This is a blocker.



回答2:

The parameter you should pass is the persistence-unit. (Not the file name.)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="1.0" ... >
    <persistence-unit name="generated">
        <class>generated.Items</class>
        <class>generated.Items$Item</class>
        <class>generated.PurchaseOrderType</class>
        <class>generated.USAddress</class>
    </persistence-unit>
</persistence>

So it should be generated for the persistence.xml above:

entityManagerFactory = Persistence.createEntityManagerFactory("generated", persistenceProperties);

For your persistence, should be:

entityManagerFactory = Persistence.createEntityManagerFactory("org.jvnet.hyperjaxb3.ejb.tests.po", persistenceProperties);