Spring事物
文章目录
- 为什么需要事务管理?
- Spring事务的核心概念
- 1. 事务传播行为
- 2. 隔离级别
- 3. 回滚规则
- 如何配置Spring事务?
- 使用@Transactional注解
- 事务失效的常见场景
- 1. 方法不是public的
- 2. 同一个类内部方法调用
- 3. 异常被捕获没抛出
- 4. 数据库不支持事务
- 总结
大家好,今天想和大家分享一下Spring框架中的事务管理。这东西在实际开发中超级实用,尤其是处理数据库操作的时候,能帮我们避免很多坑。
为什么需要事务管理?
首先,事务就是一组操作,要么全成功,要么全失败。比如你转账,从A账户扣钱,再给B账户加钱,这两个步骤必须一起完成,要是扣钱成功了加钱失败,那不就乱套了?在Spring里,事务管理主要针对数据库操作,但也能扩展到其他资源。Spring提供了两种方式:编程式事务和声明式事务。编程式就是手动写代码控制事务开始、提交、回滚;声明式更方便,通过注解或XML配置,让Spring自动帮你管。
Spring事务的核心概念
1. 事务传播行为
传播行为决定了事务在方法嵌套调用时的行为。Spring定义了7种:
传播行为 | 描述 |
---|---|
REQUIRED | 如果当前有事务,就加入;没有就新建一个。最常用。 |
REQUIRES_NEW | 总是新建一个事务,挂起当前事务。 |
NESTED | 嵌套事务,类似于REQUIRES_NEW,但回滚时只影响子事务。 |
SUPPORTS | 有事务就加入,没有就非事务执行。 |
MANDATORY | 必须有事务,否则抛异常。 |
NOT_SUPPORTED | 非事务执行,如果有就挂起。 |
NEVER | 必须非事务,否则抛异常。 |
2. 隔离级别
Spring支持数据库的隔离级别:
隔离级别 | 描述 |
---|---|
DEFAULT | 用数据库默认的。 |
READ_UNCOMMITTED | 最低级别,可能脏读。 |
READ_COMMITTED | 避免脏读,但可能不可重复读。 |
REPEATABLE_READ | 避免不可重复读,但可能幻读。 |
SERIALIZABLE | 最高级别,完全串行,但性能差。 |
实际项目中,READ_COMMITTED够用了,除非有特殊需求。
3. 回滚规则
默认情况下,Spring只在RuntimeException时回滚,Checked Exception不会。你可以用@Transactional(rollbackFor = Exception.class)
指定回滚的异常类型。
如何配置Spring事务?
Spring Boot时代,配置超级简单。主要用注解方式。
使用@Transactional注解
在Service方法上加:
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)public void transferMoney(Long fromId, Long toId, BigDecimal amount) {User from = userRepository.findById(fromId).orElseThrow();User to = userRepository.findById(toId).orElseThrow();from.setBalance(from.getBalance().subtract(amount));to.setBalance(to.getBalance().add(amount));userRepository.save(from);userRepository.save(to);// 模拟异常int i = 10 / 0;}
}
这个例子中,如果抛异常,整个转账操作都会回滚。
事务失效的常见场景
1. 方法不是public的
Spring的事务是基于AOP代理实现的,默认只对public方法生效。如果你把@Transactional加在protected、private或package-private方法上,事务不会触发。
2. 同一个类内部方法调用
假如类A有方法foo()调用bar(),bar()有@Transactional,但因为没走代理(直接this.bar()),事务不生效。
解决方案:
- 在同一个类中不要用到内部方法调用
- 用AopContext.currentProxy()走代理,但需@EnableAspectJAutoProxy(exposeProxy = true)。
3. 异常被捕获没抛出
@Transactional默认只在抛出RuntimeException时回滚。如果你try-catch了异常,没再抛,事务就提交了。
4. 数据库不支持事务
数据库不支持事务:如MyISAM引擎,用InnoDB。
总结
我在这篇文章中分享了Spring事务管理的实用经验,从为什么需要事务入手,解释了它如何确保操作的原子性,比如转账场景。接着,我详细介绍了核心概念,包括传播行为和隔离级别的表格展示,以及回滚规则。然后,我展示了如何用@Transactional注解配置事务,并举了转账代码例子。最后,我列出了事务失效的常见坑,如非public方法和类内调用问题,希望能帮大家在开发中避开雷区,更高效地处理数据库操作。