adamconkey
adamconkey

Reputation: 4745

Spring Boot + JPA inject EntityManager no EntityManager available

I am trying to annotation-inject an EntityManager into my DAO using spring boot but am getting an InvalidDataAccessApiUsageException saying there is no transactional EntityManager available. I am under the impression that Spring Boot will basically take care of everything for me as long as it has my DataSource info from application.yml and I tag the EntityManager field in my DAO with @PersitenceContext, so that I don't need an EntityManagerFactory in my own code nor a persistence.xml file. Am I wrong about that?

My DAO:

@Repository
public class PersonDaoJpa implements PersonDao {

    @PersistenceContext
    private EntityManager em;

    ...

    DAO methods

    ...
}

My Spring Boot configuration:

@SpringBootApplication
@EnableTransactionManagement
@ComponentScan(basePackages = {ProgramInfo.BASE_PACKAGE})
@EntityScan(basePackages = {ProgramInfo.BASE_PACKAGE})
public class PersonServiceConfiguration {

    public static void main(String[] args) {

        SpringApplication.run(PersonServiceConfiguration.class, args);
    }
}

My JUnit test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = PersonServiceConfiguration.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    DbUnitTestExecutionListener.class })
@EnableTransactionManagement
@DatabaseSetup("/test_data_start.xml")
public class PersonDaoJpaTest {

    @Autowired
    private PersonDaoJpa dao;

    ...

    JUnit tests 

    ...
}

My application.yml file:

spring:
    profiles.active: default

---

spring:
    profiles: default

spring.datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/database?autoReconnect=true
    username: user
    password: pwd

spring.jpa.show-sql: false
spring.jpa.database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

And finally my pom.xml file:

<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>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.2.RELEASE</version>
  </parent>  
  <groupId>myId</groupId>
  <artifactId>myArtifictId</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.github.springtestdbunit</groupId>
        <artifactId>spring-test-dbunit</artifactId>
        <version>1.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
  </dependencies>

  <repositories>
      <repository>
          <id>spring-releases</id>
          <url>https://repo.spring.io/libs-release</url>
      </repository>
  </repositories>
  <pluginRepositories>
      <pluginRepository>
          <id>spring-releases</id>
          <url>https://repo.spring.io/libs-release</url>
      </pluginRepository>
  </pluginRepositories>
</project>

Here is the full exception I get:

org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:216)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
at com.accenture.javadojo.orgchart.dao.PersonDaoJpa$$EnhancerBySpringCGLIB$$6c935a0d.flush(<generated>)
at dao.PersonDaoJpaTest.testUpdateInvalid(PersonDaoJpaTest.java:92)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Caused by: javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:275)
at com.sun.proxy.$Proxy54.flush(Unknown Source)
at com.accenture.javadojo.orgchart.dao.PersonDaoJpa.flush(PersonDaoJpa.java:63)
at com.accenture.javadojo.orgchart.dao.PersonDaoJpa$$FastClassBySpringCGLIB$$ed9d6faf.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:717)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 33 more

UPDATE: The following JUnit test passes when I comment out dao.flush():

@Test
public void testFind() {

    Person one = dao.find(1l);
    //dao.flush();
    assertNotNull("User was not found", one);
    assertEquals("Last name not as expected", "one", one.getLastName());
    assertEquals("First name not as expected", "start1", one.getFirstName());
}

but with it uncommented, I get the same exception as described above. The find and flush methods are as follows:

@Override
public Person find(Long id) {

    return em.find(Person.class, id);
}

public void flush() {

    em.flush();
}

Upvotes: 2

Views: 4179

Answers (1)

Pytry
Pytry

Reputation: 6390

There more specific you become in your implementations, the more Spring Boot will back off in what it does for you.

Because you have created your own implementations, instead of defining an interface which implements CrudRepository (for example), you will now also need to configure your own entity manager.

Here's some documentation links that can show you how to do what you need:

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

http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.create-instances.java-config

Upvotes: 1

Related Questions