Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot + Hibernate + JPA No transactional EntityManager available

I am using spring boot 1.2.3.RELEASE version with JPA over hibernate. I am experiencing following exception

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) ~[EntityManagerFactoryUtils.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223) ~[HibernateJpaDialect.class:4.1.6.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417) ~[AbstractEntityManagerFactoryBean.class:4.1.6.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) ~[ChainedPersistenceExceptionTranslator.class:4.1.6.RELEASE]
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) ~[DataAccessUtils.class:4.1.6.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) ~[PersistenceExceptionTranslationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122) ~[CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.class:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) [ExposeInvocationInterceptor.class:4.1.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [ReflectiveMethodInvocation.class:4.1.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) [JdkDynamicAopProxy.class:4.1.6.RELEASE]
at com.sun.proxy.$Proxy110.deleteByCustomerId(Unknown Source) ~[na:na]

Caused by: javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:275) ~[SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.class:4.1.6.RELEASE]
at com.sun.proxy.$Proxy102.remove(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution$DeleteExecution.doExecute(JpaQueryExecution.java:270) ~[JpaQueryExecution$DeleteExecution.class:na]
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74) ~[JpaQueryExecution.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:97) ~[AbstractJpaQuery.class:na]
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:88) ~[AbstractJpaQuery.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:395) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:373) ~[RepositoryFactorySupport$QueryExecutorMethodInterceptor.class:na]

Following is my program structure
Configuration class

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableTransactionManagement
public class WSApplication {
    public static void main(final String[] args) {
        SpringApplication.run(WSApplication.class, args);
    }
}

@Entity
@Table(Orders)
public class Order {
    @id
    @GeneratedValue
    private long id;

    @Column(name = "customerId")
    private Long customerId;

    // getter & setter methods
    // equals & hashCode methods
}

public interface OrderRepository extends JpaRepository<Order, Long> {

    List<Order> findByCustomerId(Long customerId);

    // 4- @Transactional works fine
    void deleteByCustomerId(Long cusotmerId);

}

public class OrderService {

    @Autowired
    private OrderRepository repo;

    // 3- @Transactional works fine
    public void deleteOrder(long customerId){
        //1- throws exception
        repo.deleteByCustomerId(customerId); 

        //2- following works fine
        //repo.delete(repo.findByCustomerId(customerId).get(0));
    }

}

In above service class code, can anyone please guide me why 2 works and 1 throws exception.

Thanks

like image 896
amique Avatar asked Jun 29 '15 23:06

amique


2 Answers

First, I make a quote of the Spring-Data JPA Documentation to justify why the delete method works in your case (I mean the option 2).

CRUD methods on repository instances are transactional by default. For reading operations the transaction configuration readOnly flag is set to true, all others are configured with a plain @Transactional so that default transaction configuration applies. For details see JavaDoc of CrudRepository

The delete method is actually a method of the CrudRepository. Your repository extends JpaRepository which extends CrudRespository, so it belongs to CrudRepository interface and according the quote above is transactional.

If you read the section Transactional Query Method you will see that is the same that the option 4 and you will know how to apply a custom transactional behavior for all methods of your repository. Also, the Example 61 of the documentation shows the same scenario that the option 3.

Now keep in mind that you aren't working with JDBC logic, in which case the database take care about the transactions, but within a ORM-based framework. ORM frameworks require a transaction in order to trigger the synchronization between the object cache and the database. So you must be aware and provide a transaction context for methods that do ORM logic like deleteByCustomerId.

By default @Transactional (I mean without any parameter) set propagation mode to REQUIREDand readOnly flag to false. When you invoke a method annotated within, a transaction is intialized if no-one exists. This is the reason of why the workaround of @LucasSaldanha (the same as example Using a facade to define transactions for multiple repository calls) and the option 4 works. Other wise, without a transaction, you fall in the thrown exception of the option 1.

like image 198
Guillermo Avatar answered Oct 10 '22 00:10

Guillermo


Ok I found out a way of making it works.

Just put a @Transactional annotation (org.springframework.transaction.annotation.Transactional) in your deleteOrder method at OrderService.

@Transactional
public void deleteOrder(long customerId){
    repo.deleteByCustomerId(customerId);
}

I really don't know why the second works. I guessing that since it is an direct method from the CrudRepository interface someway it knows how to execute it atomically.

The former one is a call to the deleteByCustomerId. This call will be processed to find out the customer with the specified id and then deletes it. For some reason it makes the use of an explicit transaction.

Again it is just a guess. I'll try to contact some spring developers and maybe open a issue to verify this behaviour.

Hope it helps!

Reference: http://spring.io/guides/gs/managing-transactions/

like image 39
Lucas Saldanha Avatar answered Oct 10 '22 00:10

Lucas Saldanha