13

I am days into this, and - although I am learning a lot - starting to despair.

I have tried all the suggestions on this excellent question:

No Persistence provider for EntityManager named

I had this working at one point using the ubiquitous HibernateUtil class, but was told to move to a plain JPA style here:

Spring RESTful controller method improvement suggestions

Unfortunately, I could not get the bean injection to work properly in spring-boot. Here is my attempt:

Spring JPA (Hibernate) No qualifying bean of type: javax.persistence.EntityManagerFactory

After much work down that path I ended up with a null entity manager. I found this and began to think it could not work:

Using JPA2 in Tomcat 6: @PersitenceContext doesn't work, EntityManager is null

It seems to me like the EntityManagerFactory absolutely should be a bean in whatever context spring-boot creates, but ... whatever. I would think that at least this would work:

Application launch:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Controller:

@Controller
public class GetController {

    private static final String PERSISTENCE_UNIT_NAME = "cpJpaPu";  

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public @ResponseBody User getUser(@RequestParam(value="id", required=true) int id) {
        User user = null;

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
        EntityManager em = emf.createEntityManager();
        UserDAO userDao = new UserDAO();
        userDao.setEntityManager(em);
        user = userDao.load(id);

        return user;
    }
}

DAO:

public class UserDAO {

    public EntityManager entityManager;

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }   

    public void insert(User user) {
        entityManager.persist(user);
    }

    public User load(int id) {
        return entityManager.find(User.class, id);
    }
}

/src/main/resources/persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
  <persistence-unit name="cpJpaPu" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>com.mydomain.User</class>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
      <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
      <property name="hibernate.show_sql" value="false"/>
      <property name="hibernate.connection.username" value="user"/>
      <property name="hibernate.connection.password" value=""/>
      <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/mydb"/>
    </properties>
  </persistence-unit>
</persistence>

And it doesn't work:

javax.persistence.PersistenceException: No Persistence provider for EntityManager named cpJpaPu
    javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:61)
    javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
    com.mydomain.GetController.getUser(GetController.java:25)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    java.lang.reflect.Method.invoke(Unknown Source)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:748)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:947)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:878)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:946)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:822)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)

--- Added Info ---

POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mygroup</groupId>
    <artifactId>myartifact</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>0.5.0.M6</version>
    </parent>

    <dependencies>
        <!--  Spring framework -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--  Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.0.Final</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <!-- Must override version or face stack traces -->
            <version>4.3.0.Final</version>
        </dependency>

        <!-- Spring ORM, works with Hibernate -->   
        <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-orm</artifactId>
        </dependency>

        <!--  Spring implementation of Jackson for RESTful JSON -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>         
        </dependency>

        <!--  JDBC -->
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.1-901.jdbc4</version>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>

        <!-- Prevent logging conflicts -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <scope>compile</scope>
            <exclusions>                
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions> 
        </dependency>
    </dependencies>

    <properties>
        <start-class>com.cloudfordev.controlpanel.Application</start-class>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>   
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>          
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>
Community
  • 1
  • 1
Lurk21
  • 2,307
  • 12
  • 37
  • 55
  • What happens if you use `default` as the name of the persistence unit instead? – chrylis -cautiouslyoptimistic- Dec 30 '13 at 22:20
  • Same problem @chrylis – Lurk21 Dec 30 '13 at 22:21
  • /src/main/resources is in the classpath, right? I can tell you eclipse is not excluding it when it builds the jar. – Lurk21 Dec 30 '13 at 22:51
  • Out of curiosity, why are you manually creating your EMF instead of injecting it as is usual? – chrylis -cautiouslyoptimistic- Dec 30 '13 at 23:02
  • @chrylis I couldn't get injection to work... See some of my other questions regarding Spring EntityManager / EntityManagerFactory. I would rather have the EMF as a bean, but is that possible with Tomcat / Spring-boot? I understand that DI injections may not work unless I'm in a Java-EE environment – Lurk21 Dec 30 '13 at 23:07
  • Spring is essentially a Java EE environment. Also note that unless a bean is Spring-managed, it usually won't get autowired, so even a working DI bean won't get wired if you use `new`: http://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null – chrylis -cautiouslyoptimistic- Dec 30 '13 at 23:28
  • Another unrelated comment: there is a `spring-boot-starter-log4j` if you want to use that instead of the default logback (just exclude the logging starter from the web dependency). – Dave Syer Dec 31 '13 at 10:23

5 Answers5

11

spring boot does not read persistence.xml file by default, see the document here

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html

so if you want to keep using persistence.xml file just add below code into your AppConfig class

@Bean
public LocalEntityManagerFactoryBean entityManagerFactory(){
     LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
    factoryBean.setPersistenceUnitName("cpJpaPu");
    return factoryBean;
}
vedat
  • 1,193
  • 9
  • 10
4

There are some features of JPA that only work in XML configuration unfortunately, but I can't see anything like that in yours. I don't think persistence.xml is loaded by default, so probably that's the issue. So why don't you go with the flow and use Java and application.properties to configure the entity manager? The JPA sample from Spring Boot has everything you need to get started. It uses Spring Data JPA, whereas your code is only using the JPA APIs, but you can easily strip back to that level by just removing the Spring Data dependencies in the sample.

Recent Spring Boot snapshots have a feature that lets you create your own LocalEntityManagerFactoryBean so that you can add a custom XML configuration, but up to M7 you would have to do all the JPA configuration manually once you needed a custom EntityManager.

N.B. you aren't really using dependency injection very effectively in your controller - why wouldn't you just inject the UserDao?

Dave Syer
  • 56,583
  • 10
  • 155
  • 143
4

The persistence.xml should be in the META-INF directory

/src/main/resources/META-INF/persistence.xml

xi.lin
  • 3,326
  • 2
  • 31
  • 57
  • I tried this and it doesn't work, see this post: http://stackoverflow.com/questions/40399054/persistence-xml-not-found-gradle-spring-mvc-project-with-nosql-database – kiltek Nov 03 '16 at 10:37
1

This question was answered with a much better architecture over here:

Spring JPA (Hibernate) No qualifying bean of type: javax.persistence.EntityManagerFactory

Community
  • 1
  • 1
Lurk21
  • 2,307
  • 12
  • 37
  • 55
1

I resolved this issue after deleted all hibernate-core folders under below directory: .m2\repository\org\hibernate\hibernate-core

and rebuilt my projects.

Now, it works fine under Spring Boot 2.0.4.RELEASE. And I'm sure that it loads the main/resources/META-INF/persistence.xml without injecting LocalEntityManagerFactoryBean Bean.

Before I delete them, there are 4 versions of hibernate-core in the above folder. They are "4.3.6"/"5.0.12"/"5.2.17"/"5.3.4".

After I deleted them, there are "5.0.12"/"5.2.17"/"5.3.4" after rebuilt my projects.

And when I dig into this issue, I found that the "hibernate-core-5.2.17.Final.jar" in previous "5.2.17" folder is bigger than the normal and it has no "hibernate-core-5.2.17.Final.jar.sha1".

So, it may caused by poor network or poor mirror.

capcom923
  • 638
  • 5
  • 15