你在开发系统时,有没有遇到过这种情况:转账操作中,扣钱成功了,但收款方却没收到?这种问题往往是因为程序执行到一半出错了,但前面的操作已经生效。这时候,事务处理就派上用场了。
什么是事务处理?
简单说,事务就是一组操作要么全部成功,要么全部失败。就像去便利店买东西,付款和拿货得一起完成,少一个都不行。在编程里,我们常用“事务注解”来控制这一过程。
@Transactional 注解怎么用?
在 Spring 框架中,最常用的事务注解是 @Transactional。你只需要在方法或类上加上这个注解,框架就会自动管理事务的开启、提交和回滚。
比如写一个转账方法:
@Service
public class AccountService {
@Autowired
private AccountMapper accountMapper;
@Transactional
public void transferMoney(String from, String to, BigDecimal amount) {
accountMapper.deduct(from, amount);
accountMapper.add(to, amount);
}
}
只要这个方法里有任何一步抛出异常,比如数据库连接失败或者金额为负,整个操作都会回滚,就像什么都没发生过。
哪些情况会触发回滚?
默认情况下,只有运行时异常(RuntimeException)和错误(Error)才会触发自动回滚。如果你抛的是检查异常(Exception 的子类但不是 RuntimeException),事务不会自动回滚。
比如你想在余额不足时手动回滚,可以这样写:
@Transactional
public void transferMoney(String from, String to, BigDecimal amount) throws InsufficientFundsException {
if (getBalance(from).compareTo(amount) < 0) {
throw new InsufficientFundsException("余额不足");
}
accountMapper.deduct(from, amount);
accountMapper.add(to, amount);
}
这时候要让事务回滚,就得显式声明:
@Transactional(rollbackFor = Exception.class)
public void transferMoney(...) throws InsufficientFundsException { ... }
注意这些细节
事务注解不是加了就万事大吉。同一个类里的方法调用,如果不通过代理对象,事务可能不生效。比如 A 方法调用本类的 B 方法,而 B 加了 @Transactional,这时候事务很可能被忽略。
还有,事务只对数据库操作有效。如果你在方法里发邮件、上传文件,这些外部操作不会被事务管理,出错了也撤不回来。得自己额外处理。
另外,别忘了配置数据源支持事务。MySQL 用 InnoDB 引擎才行,MyISAM 就不行。就像你买了智能锁,结果门没装对,再高级也没用。