首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring Boot 企业级事务设计规范(90% 项目都会踩的坑)

Spring Boot 企业级事务设计规范(90% 项目都会踩的坑)

作者头像
井九
发布2026-03-11 08:53:01
发布2026-03-11 08:53:01
340
举报
文章被收录于专栏:四楼没电梯四楼没电梯

在企业级 Spring Boot 项目中,@Transactional 是最常用但最容易被误用的注解之一。

很多系统上线后才发现:

  • 事务根本没有生效
  • 数据部分提交
  • 分布式锁 + 事务顺序错误
  • MyBatis 自动提交

本文总结一套 企业级事务设计规范,包含:

1️⃣ @Transactional 8 大常见失效场景 2️⃣ 事务传播最佳实践 3️⃣ MyBatis + Spring 事务机制原理 4️⃣ 分布式锁 + 事务顺序问题

在这里插入图片描述
在这里插入图片描述

一、Spring 事务架构

Spring 事务本质是 AOP + 数据源事务管理器

执行流程:

代码语言:javascript
复制
调用 Service 方法
      ↓
Spring AOP 拦截
      ↓
开启事务
      ↓
执行 SQL
      ↓
成功 → commit
异常 → rollback

二、@Transactional 8 大常见失效场景

这是生产事故最多的 8 种情况


1 同类方法调用(最常见)

事务依赖 AOP代理

如果在同一个类中调用事务方法:

代码语言:javascript
复制
@Service
public class UserService {

    public void methodA() {
        methodB(); // 事务不会生效
    }

    @Transactional
    public void methodB() {
        userMapper.insert();
    }
}

原因:

解决方案:

代码语言:javascript
复制
@Autowired
private UserService userService;

public void methodA(){
    userService.methodB(); // 通过代理调用
}

或:

代码语言:javascript
复制
AopContext.currentProxy()

2 方法不是 public

Spring 默认只代理 public 方法

错误示例:

代码语言:javascript
复制
@Transactional
private void saveUser(){
}

正确:

代码语言:javascript
复制
@Transactional
public void saveUser(){
}

3 捕获异常但没有抛出

Spring 只在 异常抛出 时回滚。

错误示例:

代码语言:javascript
复制
@Transactional
public void createOrder(){

    try{
        orderMapper.insert();
        int a = 1 / 0;
    }catch(Exception e){
        log.error(e);
    }

}

结果:

代码语言:javascript
复制
订单数据提交成功

正确写法:

代码语言:javascript
复制
catch(Exception e){
    throw new RuntimeException(e);
}

或者:

代码语言:javascript
复制
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

4 异常不是 RuntimeException

Spring 默认只回滚:

代码语言:javascript
复制
RuntimeException
Error

不会回滚:

代码语言:javascript
复制
CheckedException

错误示例:

代码语言:javascript
复制
@Transactional
public void test() throws Exception {
    throw new Exception("error");
}

解决:

代码语言:javascript
复制
@Transactional(rollbackFor = Exception.class)

5 Controller 层加事务

很多人这样写:

代码语言:javascript
复制
@RestController
public class OrderController {

    @Transactional
    @PostMapping("/create")
    public void create(){
        orderService.create();
    }
}

问题:

  • Controller 事务范围太大
  • HTTP逻辑 + DB逻辑混合

正确:

事务放在:

代码语言:javascript
复制
@Service
public class OrderService {

    @Transactional
    public void create(){
        orderMapper.insert();
    }

}

6 使用 new 创建对象

错误示例:

代码语言:javascript
复制
UserService userService = new UserService();
userService.save();

Spring AOP 无法代理:

代码语言:javascript
复制
事务失效

必须:

代码语言:javascript
复制
@Autowired
UserService userService;

7 多线程调用

代码语言:javascript
复制
@Transactional
public void createOrder(){

    new Thread(() -> {
        orderMapper.insert();
    }).start();

}

事务只在当前线程有效。


8 final 方法

Spring 默认 JDK/CGLIB 代理

final 方法无法被代理:

代码语言:javascript
复制
@Transactional
public final void save(){
}

事务失效。


三、事务传播机制

事务传播用于:

代码语言:javascript
复制
事务嵌套
微服务调用
日志记录
补偿操作

Spring 支持 7 种传播行为。

传播级别

说明

REQUIRED

默认,加入当前事务

REQUIRES_NEW

新事务

SUPPORTS

有事务就加入

NOT_SUPPORTED

不使用事务

MANDATORY

必须有事务

NEVER

不能有事务

NESTED

嵌套事务


推荐企业实践

1 默认使用 REQUIRED

代码语言:javascript
复制
@Transactional
public void createOrder(){
}

2 日志使用 REQUIRES_NEW

保证日志一定提交。

代码语言:javascript
复制
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(){
}

架构:

即使主事务回滚:

代码语言:javascript
复制
日志仍然存在

3 审计记录

代码语言:javascript
复制
REQUIRES_NEW

适用于:

代码语言:javascript
复制
操作日志
审计日志
系统日志

四、MyBatis + Spring 事务机制

很多人误以为:

代码语言:javascript
复制
MyBatis 自动管理事务

实际上:

代码语言:javascript
复制
Spring 管理事务
MyBatis 只执行 SQL

架构:

关键组件:

代码语言:javascript
复制
DataSourceTransactionManager
SqlSessionTemplate

SqlSessionTemplate

Spring 会自动替换 MyBatis SqlSession:

代码语言:javascript
复制
SqlSessionTemplate

作用:

代码语言:javascript
复制
保证同一事务使用同一连接

流程:


五、分布式锁 + 事务顺序问题(重大坑)

很多系统这样写:

代码语言:javascript
复制
@Transactional
public void createOrder(){

    RLock lock = redisson.getLock("order");

    lock.lock();

    orderMapper.insert();

    lock.unlock();

}

问题:

代码语言:javascript
复制
锁释放了
事务还没提交

可能出现:

代码语言:javascript
复制
并发数据错误

正确顺序:

示例:

代码语言:javascript
复制
RLock lock = redisson.getLock("order");

lock.lock();

try{

    orderService.createOrder(); // 事务方法

}finally{

    lock.unlock();

}

六、企业级事务设计规范

总结一套标准架构:

设计原则:

1 事务只放 Service
代码语言:javascript
复制
Controller 不加事务

2 事务范围最小

不要:

代码语言:javascript
复制
HTTP调用
RPC调用
文件操作

3 锁在事务外层

顺序:

代码语言:javascript
复制
Lock
 → Transaction
 → SQL
 → Commit
 → Unlock

4 日志使用独立事务
代码语言:javascript
复制
REQUIRES_NEW

5 禁止在事务中调用远程接口

例如:

代码语言:javascript
复制
RPC
HTTP
MQ

原因:

代码语言:javascript
复制
事务时间过长
数据库锁等待

七、企业级事务架构模板

推荐 Service 模板:

代码语言:javascript
复制
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private LogService logService;

    @Transactional
    public void createOrder(){

        orderMapper.insertOrder();

        logService.saveLog();

    }

}

日志:

代码语言:javascript
复制
@Service
public class LogService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(){

        logMapper.insert();

    }

}

常用工具

App Store 截图生成器 、在线图片压缩 乖猫记账,AI智能分类的最佳聊天学生必备记账App。 百度网盘免费加速


总结

企业级 Spring 事务设计核心原则:

代码语言:javascript
复制
1 事务只放 Service
2 默认 REQUIRED
3 日志 REQUIRES_NEW
4 锁在事务外
5 避免远程调用
6 注意 AOP 代理限制

牢记一句话:

Spring 事务不是数据库功能,而是 AOP 代理。

只要理解这一点,80% 的事务问题都能解决

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Spring 事务架构
  • 二、@Transactional 8 大常见失效场景
  • 1 同类方法调用(最常见)
  • 2 方法不是 public
  • 3 捕获异常但没有抛出
  • 4 异常不是 RuntimeException
  • 5 Controller 层加事务
  • 6 使用 new 创建对象
  • 7 多线程调用
  • 8 final 方法
  • 三、事务传播机制
  • 推荐企业实践
    • 1 默认使用 REQUIRED
    • 2 日志使用 REQUIRES_NEW
    • 3 审计记录
  • 四、MyBatis + Spring 事务机制
    • SqlSessionTemplate
  • 五、分布式锁 + 事务顺序问题(重大坑)
  • 六、企业级事务设计规范
    • 1 事务只放 Service
    • 2 事务范围最小
    • 3 锁在事务外层
    • 4 日志使用独立事务
    • 5 禁止在事务中调用远程接口
  • 七、企业级事务架构模板
  • 常用工具
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档