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

Posted

技术标签:

【中文标题】Spring boot + Hibernate + JPA 没有可用的事务性 EntityManager【英文标题】:Spring boot + Hibernate + JPA No transactional EntityManager available 【发布时间】:2015-09-16 14:28:04 【问题描述】:

我使用 Spring Boot 1.2.3.RELEASE 版本和 JPA over hibernate。我遇到以下异常

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]

以下是我的程序结构 配置类

@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));
    


在上面的服务类代码中,谁能指导我为什么 2 工作和 1 抛出异常。

谢谢

【问题讨论】:

【参考方案1】:

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

默认情况下,存储库实例上的 CRUD 方法是事务性的。为了 读取操作设置了事务配置readOnly 标志 为真,所有其他人都配置了一个普通的@Transactional,这样 默认事务配置适用。有关详细信息,请参阅 JavaDoc CrudRepository

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

如果您阅读Transactional Query Method 部分,您会看到与选项4 相同,并且您将知道如何为存储库的所有方法应用自定义事务行为。 此外,文档的示例 61 显示了与选项 3 相同的场景。

现在请记住,您不是在使用 JDBC 逻辑,在这种情况下,数据库会处理事务,而是在基于 ORM 的框架内。 ORM 框架需要一个事务来触发对象缓存和数据库之间的同步。 因此,您必须了解并为执行 ORM 逻辑的方法(如 deleteByCustomerId)提供事务上下文。

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

【讨论】:

文档应该比已经完成的更详细地提及这一点。感谢您的帮助 我猜你指的是 Example 81 而不是 Example 61【参考方案2】:

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

只需在 OrderServicedeleteOrder 方法中添加 @Transactional 注释 (org.springframework.transaction.annotation.Transactional)。

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

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

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

这只是一个猜测。我会尝试联系一些 Spring 开发人员,并可能会打开一个问题来验证这种行为。

希望对你有帮助!

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

【讨论】:

谢谢,但我更关心为什么 deleteByCustomerId 不起作用。根据我的理解,spring 应该像 delete 方法一样自动启动事务。 为什么会这样,只有超级方法是事务性的,如果您希望自己的方法具有事务性,则必须将其标记为这样。旁边的答案实际上是正确的(恕我直言),因为您的事务边界应该是服务调用而不是 dao 调用。【参考方案3】:

即使在使用@Transactional 注释我的search() 方法后,我仍然得到No transactional EntityManager available 异常。

我关注了this tutorial,它描述了如何在 Spring Boot 中设置 Hibernate 搜索。

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

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

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

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

【讨论】:

这个对我有用,我正在使用 5.11.0.Final 并通过降级到 5.7.0.Final 我让它工作了

以上是关于Spring boot + Hibernate + JPA 没有可用的事务性 EntityManager的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot/Hibernate 创建表失败

Spring Boot JPA Hibernate - 以毫秒精度存储日期

spring boot DAO之Hibernate

制作多个 EntityManager(Spring-Boot-JPA、Hibernate、MSSQL)

Spring Boot ManyToMany - *** - JPA,Hibernate

Hibernate 和 CRUDRepository Spring Boot