Spring @Transactional 对服务和非服务方法的不同行为?

Posted

技术标签:

【中文标题】Spring @Transactional 对服务和非服务方法的不同行为?【英文标题】:Spring @Transactional different behavior on Service and non-Service methods? 【发布时间】:2021-12-05 10:06:31 【问题描述】:

我有一个具有标准架构(控制器 -> 服务 -> 存储库)的 Spring Boot 2.3 REST 应用程序。出于审计目的,我插入了一个薄层(某种Mediator),以便我将所有请求持久化到某个特定的服务方法,无论它们是否成功持久化或抛出异常并回滚事务。示例:

@Component
public class Mediator 

    private final Service service;
    private final AuditService auditService;

    public Mediator(Service service, AuditService auditService) 
        this.service = service;
        this.auditService = auditService;
    

    @Transactional
    public void saveReport(Report report) 
        try 
            service.saveReport(report);
            auditService.saveReport(report); 
         catch (Exception exception) 
            auditService.saveReport(report, exception);
            throw exception;
        
    

因此我遇到了一个奇怪的情况:如果我将@Transactional 放在Mediator 的方法上(上面的示例),JPA Repositories 中的所有操作都会成功持久化。如果我将 @Transactional 放在 ServiceImpl 方法上(下面的示例)并且不再放在 Mediator 上,则不会运行删除查询之一,尽管其余查询执行得很好。假设我的 ServiceImpl 看起来像:

@Service
public class ServiceImpl implements Service 

    private final RepositoryA repositoryA;
    private final RepositoryB repositoryB;

    public ServiceImpl(RepositoryA repositoryA, RepositoryB repositoryB) 
        this.repositoryA = repositoryA;
        this.repositoryB = repositoryB;
    

    @Transactional
    public void saveReport(Report report) 
        repositoryA.save(report.getA());
        repositoryB.save(report.getB());
        repositoryB.delete(report.getSomethingElse());
    

关于事务的两种方法之间唯一明显的区别是,在第一种情况下,我对每个 Mediator 调用都有这个:

o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(909894553<open>)] for JPA transaction

在第二种情况下,我有这个:

tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation

我猜想用@Transactional(我在调解器中所做的)直接注释bean的方法和用同样的事情(我们通常通过注释 ServiceImpl 方法来做),但我不确定也无法找出这种奇怪行为的原因。有谁知道为什么会这样?

【问题讨论】:

请查看以下内容:***.com/questions/8224465/…***.com/questions/17772614/… @SusanMustafa 这些问题有助于解释实现接口的 bean 和不实现接口的 bean 之间的区别,但我无法找出应用程序中奇怪行为的原因 这可能是因为您直接使用存储库,因此它使用 SharedEntityManagerInvocationHandler。您可以通过让 Mediator 直接调用存储库来测试这个理论吗?而不是调用服务..看看你是否得到相同的。这样我们就可以消除在这种情况下将实现接口作为一个因素的可能性。仍然很有趣,我很想知道你得到了什么。 @SusanMustafa 在现实世界的示例中,对存储库方法的调用是通过调用另一个服务来完成的,该服务注入了带有 Transactional 注释(或未注释)的服务。我直接在第一个服务(示例中的那个)中使用了 DAO 方法,但仍然无济于事。实体保留在数据库中。 您是使用带有@PersistenceContext 的EntityManager 还是它的默认jpa entitymanager? 【参考方案1】:

我猜这种行为差异是由于默认启用的 Spring OpenSessionInView。 您必须在 application.yml 中设置

spring:
  jpa:
    open-in-view: false

请看OSIV

【讨论】:

以上是关于Spring @Transactional 对服务和非服务方法的不同行为?的主要内容,如果未能解决你的问题,请参考以下文章

@Transactional注解详细使用

Spring Data Redis - 对 Repository 的 @Transactional 支持

如何在 Spring Boot 中使用 @Transactional 注解

Spring 忽略 @Transactional 注释

Spring - 2 个应用程序可以同时使用相同的 @Transactional 服务吗?

Spring 之@Transactional篇