SpringBoot使用事务时有哪些注意 ?

一. 声明式注解与编程式事务

1.1 说明

  • 声明式事务通过手动注解或xml配置的方式, 通过spring来管理事务. 声明式事务属于无侵入式,不会影响业务逻辑的实现. 是spring推荐的, 也是主流做法.
  • 编程式事务需要手动开启, 手动控制回滚, 手动提交. 且每次重新实现. spring并不推荐.

1.2 从编程式事务改造成声明式事务

二. 事务怎么加, 什么时候会失效 ?

事务加到哪里? controller层, service层, dao层, 还是都加? 加到方法上还是加到类上? 加了为什么会失效?

2.1 建议

  1. 在事务执行之前, 事务逻辑无关的数据准备好,比如流水号, 待更新的库存量, 字典数据.
  2. 把事务管理的多个SQL封装到一个方法. 放在dao层. 给该方法加上@Transaction
  3. 让事务逻辑尽量短小精悍 🙃

2.2 事务失效的常见原因

2.2.1 方法内部调用

1
2
3
4
5
6
7
8
9
10
@Transactional
public int updateTable3() {
ypxxDao.updateDB();
return 0;
}

public int updateTable4() {
updateTable3();
return 0;
}

此时 controller 调用 updateTable3 事务是生效的
如果由 updateTable4 调用 updateTable3 方法. 此时 updateTable3 的事务是失效了.

这是因为 updateTable3 方法拥有事务的能力是因为 spring 生成了 updateTable3 的代理对象. 直接调用 this 对象的方式就失效了.

2.2.2 被private修饰

1
2
3
4
5
@Transactional
private int updateTable3() {
ypxxDao.updateDB();
return 0;
}

这个好理解. spring 要求方法必须为 public

2.2.3 异常被异常处理了

1
2
3
4
5
6
7
8
9
10
11
12
13
@Transactional
public int updateTable5() throws Exception {
try {
ypxxDao.update1();
if (true) {
throw new YXException("bad");
}
ypxxDao.update2();
} catch (YXException e) {
throw new Exception();
}
return 0;
}

注意:
1. 捕获异常之后需要再次抛出 runtimeException, 不然 spring 认为没有异常.
2. 抛出 Exception 同理. spring 也不会回滚

三. 事务的传播行为 @Transactional(propagation = Propagation.REQUIRED)

一共有7个. 可以网上查阅.

这里总结工作中遇到的2个.

3.1 REQUIRED

  1. 全部成功才提交. 也是默认值. 为了保证数据完整性. 所有sql执行完成才提交数据.

3.2 REQUIRES_NEW

  1. 新建事务, 执行成功先提交, 当前存在事务, 新建事务执行异常则回滚外部事务
  2. 一般是请求外部接口时会用到, 比如我们请求三方数据, 对方返回了消息. 程序应该优先把数据保存到数据库. 后续逻辑不应该影响消息回写.