C 一、@Transactional 是什么?
@Transactional 是 Spring 框架提供的声明式事务管理注解,它能将多个数据库操作包装成一个原子性的工作单元——要么全部成功提交,要么全部失败回滚。
以用户注册为例:我们需要向用户表插入记录、初始化用户积分、发送欢迎消息。使用 @Transactional 后,如果积分初始化失败,用户记录会自动回滚,避免产生”僵尸用户”。
事务注解标注在方法或类上,Spring 容器会在运行时自动创建代理对象,在目标方法执行前后加入事务管理逻辑。
C 二、@Transactional 不是数据库连接管理
许多人误以为 @Transactiona l 是”数据库连接池” #技术分享或”SQL 执行器”,实则完全不同:
- 数据库连接 :负责与数据库建立通信,执行 SQL 语句
- 事务管理 :保证一组 SQL 操作的原子性、一致性、隔离性和持久性(ACID)
Spring 事务背后依赖两大核心技术:
- AOP(面向切面编程) :通过动态代理在方法执行前后插入事务逻辑
- 事务同步管理器 :通过 ThreadLocal 将事务资源绑定到当前线程
这使得多个数据库操作可以在同一个事务上下文中协同工作。
三、事务传播机制:灵活的事务边界控制
Spring 提供了 7 种事务传播行为,例如:
@Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional(propagation = Propagation.NESTED)
每个传播级别对应不同的业务场景:
- REQUIRED :大多数业务方法,需要事务保障
- REQUIRES_NEW :日志记录、消息发送等,不受主事务影响
- NESTED :复杂业务流程,允许子流程独立回滚
C 四、如何使用 @Transactional?
1. 单方法事务控制
@Service
public class UserService {
@Transactional
public void registerUser(User user) {
userDao.insert(user);
积分Service.initPoints(user.getId());
messageService.sendWelcome(user.getEmail());
}
}
如果需要自定义事务行为(如超时时间、隔离级别),通过注解参数配置:
- timeout :事务超时时间(秒)
- isolation :事务隔离级别
- rollbackFor :指定回滚的异常类型
2. 多方法事务协调
复杂业务一般涉及多个服务协作。Spring 通过事务传播机制自动管理:
@Service
@Transactional
public class OrderService {
@Autowired
private InventoryService inventoryService;
public void createOrder(Order order) {
orderDao.save(order);
inventoryService.deductStock(order);
paymentService.processPayment(order);
}
}
服务间的事务边界由传播级别决定,Spring 自动完成事务的挂起、恢复和资源同步。
为什么同类方法调用导致 @Transactional 失效?
在 Spring 框架中,@Transactional 注解用于声明式事务管理,超级方便。但有一个常见的“陷阱”:
当一个类中的方法 A 调用同一个类中的另一个带有 @Transactional 注解的方法 B 时,B 上的事务注解不会生效。
无论 B 的事务传播行为是 REQUIRED 、REQUIRES_NEW 还是 NESTED ,只要它是被 同一个类的内部方法直接调用(即“自调用”) ,Spring 的事务机制就 无法拦截到这个调用 ,因此事务不会按预期工作。
根本缘由:Spring 事务基于代理(Proxy)
Spring 的 @Transactional 是通过 AOP(面向切面编程) 实现的,默认使用 JDK 动态代理 或 CGLIB 代理 。
- 当你从 Spring 容器中获取一个 Bean(列如 MyService ),你拿到的实则是一个 代理对象 。
- 当你调用 myService.methodA() 时,代理对象会先检查该方法是否有 @Transactional ,如果有,就开启事务,再调用目标方法。
- 但是 :如果 methodA() 内部直接调用 this.methodB() ,这个调用是 绕过代理对象 的,直接在原始对象(target)上执行, AOP 拦截器根本不会介入 。
因此:
- methodB() 上的 @Transactional 不会被 Spring 感知到 。
- 事务行为完全由外层方法(如 methodA() )决定(如果它有事务的话)。
@Transactional 对 private 、final 、static 方法无效(由于代理无法覆盖)。
默认只对 unchecked exception(RuntimeException 及 Error) 回滚,checked exception 不回滚(除非配置 rollbackFor )。
使用 REQUIRES_NEW 或 NESTED 时,务必确保调用路径经过代理,否则行为不符合预期。
















暂无评论内容