You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring JPA @Transactional REQUIRES_NEW事务传播不生效问题

问题根本原因

你遇到的REQUIRES_NEW事务传播配置不生效、日志打印Participating in existing transaction,核心是Spring声明式事务的实现机制限制,具体有两个直接诱因:

  • 同类内方法调用不会经过AOP代理:Spring的@Transactional是基于动态代理织入事务逻辑的,只有通过Spring容器生成的代理对象调用方法时,事务切面才会生效。你在addNewProduct方法中直接调用本类的saveAudit()方法,本质是调用原生this对象的方法,完全绕过了代理对象,方法上的事务注解根本没有被解析执行。
  • 事务方法访问权限不符合要求:你定义的saveAudit()方法没有加public修饰符,使用的是包级默认访问权限,Spring AOP默认只会拦截public修饰的方法,非public方法上的@Transactional注解不会被识别。

额外说明:直接在Controller类上加@Transactional属于分层设计不规范,事务逻辑应该放在业务Service层,Web层只负责请求响应处理,不应该承载事务控制逻辑,但这不是本次配置失效的直接原因。另外代码里ProductreRepository、Auditrepository属于变量拼写错误,会直接导致注入失败,需要修正为和Repository接口名匹配的命名。

修复方案

按照优先级推荐以下修复步骤:

  1. 将审计日志逻辑抽离到独立的Service类中,通过注入的代理对象调用,从根源避免同类调用绕过代理的问题:
    @Service
    public class AuditService {
        private final AuditRepository auditRepository;
    
        // 构造函数注入
        public AuditService(AuditRepository auditRepository) {
            this.auditRepository = auditRepository;
        }
    
        // 必须加public修饰,明确指定传播级别为REQUIRES_NEW
        @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
        public void saveAudit() {
            auditRepository.save(Audit.builder().action("action1").build());
        }
    }
    
  2. 调整Controller代码,移除类上的@Transactional注解,注入AuditService调用审计方法,事务控制下沉到Service层:
    @RestController
    public class ProductController {
    
        private final ProductRepository productRepository;
        private final AuditService auditService;
    
        // 构造注入
        public ProductController(ProductRepository productRepository, AuditService auditService) {
            this.productRepository = productRepository;
            this.auditService = auditService;
        }
    
        @PostMapping(ApiPaths.PRODUCTS)
        @ResponseStatus(HttpStatus.CREATED)
        public ProductDTO addNewProduct() {
            ProductDTO res =  productRepository.save(Product.builder().label("product1").build());
            // 调用注入的代理对象的方法,事务切面会正常拦截
            auditService.saveAudit();
            int h=1/0; // 此处抛出异常只会回滚Product的保存事务,已经提交的Audit事务不受影响
            return res;
        }
    }
    
  3. (不推荐)如果不想抽离独立Service,也可以在Controller中注入自身的代理对象,通过代理对象调用saveAudit()方法,同时把saveAudit改为public修饰,但这种方式不符合分层规范,容易产生循环依赖问题,仅作为临时方案使用。

修复后再运行,saveAudit会正常挂起外层Product保存的事务,开启独立新事务执行审计日志保存并自动提交,后续外层抛出异常时,已经提交的审计日志不会被回滚,符合预期的事务隔离效果。

内容的提问来源于stack exchange,提问作者AHmedRef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 01:33:33