Java Spring boot + Hibernate + JPA 没有可用的事务性 EntityManager

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31127309/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 10:40:40  来源:igfitidea点击:

Spring boot + Hibernate + JPA No transactional EntityManager available

javaspringhibernatejpaspring-boot

提问by amique

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

我在休眠状态下使用带有 JPA 的 spring boot 1.2.3.RELEASE 版本。我遇到以下异常

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.

在上面的服务类代码中,任何人都可以指导我为什么 2 工作和 1 抛出异常。

Thanks

谢谢

采纳答案by Guillermo

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

首先,我引用了Spring-Data JPA 文档来证明为什么该delete方法适用于您的情况(我的意思是选项2)。

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

存储库实例上的 CRUD 方法默认是事务性的。对于读取操作,事务配置readOnly标志设置为 true,所有其他操作都使用普通配置,@Transactional以便应用默认事务配置。有关详细信息,请参阅 CrudRepository 的 JavaDoc

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

delete方法实际上是CrudRepository. 您的存储库扩展JpaRepositorywhich extends CrudRespository,因此它属于 CrudRepository 接口,并且根据上面的引用是事务性的。

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

如果您阅读事务查询方法部分,您将看到与选项4相同的内容,并且您将知道如何为存储库的所有方法应用自定义事务行为。此外,文档的示例 61显示了与选项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.

现在请记住,您不是在使用 JDBC 逻辑,在这种情况下,数据库会处理事务,而是在基于 ORM 的框架内。ORM 框架需要一个事务来触发对象缓存和数据库之间的同步。因此,您必须了解并为执行 ORM 逻辑的方法(如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 workaroundof @LucasSaldanha (the same as example Using a facade to define transactions for multiple repository calls) and the option 4works. Other wise, without a transaction, you fall in the thrown exception of the option 1.

默认情况下@Transactional(我的意思是没有任何参数)将传播模式设置为REQUIRED并将 readOnly 标志设置为 false。当您调用其中注释的方法时,如果不存在事务,则会初始化一个事务。这就是为什么@LucasSaldanha的解决方法(与示例使用外观为多个存储库调用定义事务相同)和选项4有效的原因。否则,如果没有事务,您就会陷入选项1的抛出异常中。

回答by Lucas Saldanha

Ok I found out a way of making it works.

好的,我找到了一种使它起作用的方法。

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

只需在OrderService@TransactionaldeleteOrder方法中放置一个注释(org.springframework.transaction.annotation.Transactional)。

@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 CrudRepositoryinterface someway it knows how to execute it atomically.

我真的不知道为什么第二个有效。我猜因为它是来自CrudRepository接口的直接方法,所以它知道如何以原子方式执行它。

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.

前一个是调用deleteByCustomerId。将处理此调用以找出具有指定 id 的客户,然后将其删除。出于某种原因,它使用了显式事务。

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

同样,这只是一个猜测。我会尝试联系一些 spring 开发人员,也许会打开一个问题来验证这种行为。

Hope it helps!

希望能帮助到你!

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

参考:http: //spring.io/guides/gs/managing-transactions/

回答by Patrick

I still got the No transactional EntityManager availableexception even after annotating my search()method with @Transactional.

我还是得到了No transactional EntityManager available,即使我的注释后例外search()@Transactional

I followed this tutorialwhich describes how to set up Hibernate search in Spring Boot.

我遵循了本教程该教程描述了如何在 Spring Boot 中设置 Hibernate 搜索。

The issue for me was that I had a different dependency on hibernate-search-orm. The dependency which worked for me without any problems was

对我来说,问题是我对hibernate-search-orm. 对我来说没有任何问题的依赖是

compile("org.hibernate:hibernate-search-orm:5.7.0.Final")

After adding this to the gradle build file, everything worked as expected.

将此添加到 gradle 构建文件后,一切都按预期工作。

Hope this helps someone else as well.

希望这对其他人也有帮助。