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

很多系统上线后才发现:

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

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

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


一、Spring 事务架构

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

Controller

Service

AOPProxy

TransactionInterceptor

DataSourceTransactionManager

JDBC

Database

执行流程:

调用 Service 方法
      ↓
Spring AOP 拦截
      ↓
开启事务
      ↓
执行 SQL
      ↓
成功 → commit
异常 → rollback

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

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


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

事务依赖 AOP代理

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

@Service
public class UserService {

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

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

原因:

未经过AOP代理

methodA

methodB

事务失效

解决方案:

@Autowired
private UserService userService;

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

或:

AopContext.currentProxy()

2 方法不是 public

Spring 默认只代理 public 方法

错误示例:

@Transactional
private void saveUser(){
}

正确:

@Transactional
public void saveUser(){
}

3 捕获异常但没有抛出

Spring 只在 异常抛出 时回滚。

错误示例:

@Transactional
public void createOrder(){

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

}

结果:

订单数据提交成功

正确写法:

catch(Exception e){
    throw new RuntimeException(e);
}

或者:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

4 异常不是 RuntimeException

Spring 默认只回滚:

RuntimeException
Error

不会回滚:

CheckedException

错误示例:

@Transactional
public void test() throws Exception {
    throw new Exception("error");
}

解决:

@Transactional(rollbackFor = Exception.class)

5 Controller 层加事务

很多人这样写:

@RestController
public class OrderController {

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

问题:

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

正确:

Controller

Service

Mapper

事务放在:

@Service
public class OrderService {

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

}

6 使用 new 创建对象

错误示例:

UserService userService = new UserService();
userService.save();

Spring AOP 无法代理:

事务失效

必须:

@Autowired
UserService userService;

7 多线程调用

@Transactional
public void createOrder(){

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

}

事务只在当前线程有效。

MainThread

Transaction

SubThread

NoTransaction


8 final 方法

Spring 默认 JDK/CGLIB 代理

final 方法无法被代理:

@Transactional
public final void save(){
}

事务失效。


三、事务传播机制

事务传播用于:

事务嵌套
微服务调用
日志记录
补偿操作

Spring 支持 7 种传播行为。

传播级别 说明
REQUIRED 默认,加入当前事务
REQUIRES_NEW 新事务
SUPPORTS 有事务就加入
NOT_SUPPORTED 不使用事务
MANDATORY 必须有事务
NEVER 不能有事务
NESTED 嵌套事务

推荐企业实践

1 默认使用 REQUIRED

@Transactional
public void createOrder(){
}

2 日志使用 REQUIRES_NEW

保证日志一定提交。

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

架构:

CreateOrder

SaveOrder

SaveLog

NewTransaction

即使主事务回滚:

日志仍然存在

3 审计记录

REQUIRES_NEW

适用于:

操作日志
审计日志
系统日志

四、MyBatis + Spring 事务机制

很多人误以为:

MyBatis 自动管理事务

实际上:

Spring 管理事务
MyBatis 只执行 SQL

架构:

Service

SpringTransaction

SqlSession

MyBatisExecutor

JDBC

Database

关键组件:

DataSourceTransactionManager
SqlSessionTemplate

SqlSessionTemplate

Spring 会自动替换 MyBatis SqlSession:

SqlSessionTemplate

作用:

保证同一事务使用同一连接

流程:

JDBC SqlSessionTemplate TransactionManager Service JDBC SqlSessionTemplate TransactionManager Service 开启事务 执行SQL 使用同一连接 commit

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

很多系统这样写:

@Transactional
public void createOrder(){

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

    lock.lock();

    orderMapper.insert();

    lock.unlock();

}

问题:

锁释放了
事务还没提交

可能出现:

并发数据错误

正确顺序:

获取分布式锁

开启事务

执行SQL

提交事务

释放锁

示例:

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

lock.lock();

try{

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

}finally{

    lock.unlock();

}

六、企业级事务设计规范

总结一套标准架构:

Controller

Service

Transaction

Mapper

Database

设计原则:

1 事务只放 Service

Controller 不加事务

2 事务范围最小

不要:

HTTP调用
RPC调用
文件操作

3 锁在事务外层

顺序:

Lock
 → Transaction
 → SQL
 → Commit
 → Unlock

4 日志使用独立事务

REQUIRES_NEW

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

例如:

RPC
HTTP
MQ

原因:

事务时间过长
数据库锁等待

七、企业级事务架构模板

推荐 Service 模板:

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private LogService logService;

    @Transactional
    public void createOrder(){

        orderMapper.insertOrder();

        logService.saveLog();

    }

}

日志:

@Service
public class LogService {

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

        logMapper.insert();

    }

}

常用工具

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


总结

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

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

牢记一句话:

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

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

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐