首页 > 解决方案 > Spring @Transactional 对服务和非服务方法的不同行为?

问题描述

我有一个具有标准架构(控制器-> 服务-> 存储库)的 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(我在 Mediator 中所做的)注释 bean 的方法和用相同的东西(我们通常通过注释 ServiceImpl 方法来注释bean 的(即注入接口的实现)方法)之间存在区别),但我不确定也无法找出这种奇怪行为的原因。有谁知道为什么会这样?

标签: javaspringjpaspring-data-jpaentitymanager

解决方案


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

spring:
  jpa:
    open-in-view: false

请参阅OSIV


推荐阅读