SpringBoot 使用 @Transactional 无效

在 Spring Boot 引入的依赖 spring-boot-starter、spring-boot-starter-web 中都已经包含了对于 spring-boot-starter-jdbc 或 spring-boot-starter-data-jpa 的依赖,框架会自动默认分别注入 DataSourceTransactionManager 或 JpaTransactionManager。我们不需要任何额外配置就可以用 @Transactional 注解进行事务的使用。

Spring 事物分类

编程式事物

通过使用 TransactionTemplate 手动管理事务,实际应用中很少使用。

声明式事物(代码侵入少)

分为两种情况:一是通过传统 xml 方式配置,二是使用 @Transaction 注解方式,Spring 的声明式事务是通过 AOP 实现的。

Spring 事务失效场景

数据库引擎不支持

数据库引擎要支持事务,如果是 MySQL,表要使用支持事务的引擎比如 InnoDB,如果是 MyISAM, 事务是不起作用的

Sping 包扫描的问题

检查 Spring 是否扫描到了需要开启事务的 类 / 方法所在包路径

@Transaction 注解在非 public 方法上

@Transactional 注解只能应用到 public 可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。

Service 类内部方法调用

Service 中方法 A 内部调用方法 B, 方法 A 没有事务管理,方法 B 采用了声明式事务。在方法 A 中调用方法 B,实际上是通过 this 的引用,也就是直接调用了目标类的方法,而非通过 Spring 上下文获得的代理类,所以事务不生效。

遇到检查异常,事务不回滚

遇到检查异常时,事务开启,也无法回滚。例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚

1
2
3
4
5
6
7
8
9
10
11
12
@Transactional
public int insert(User user) throws Exception {

int rows = insertUser(user);
insertUserRole(user);
// 模拟抛出 SQLException 异常
boolean flag = true;
if (flag) {
throw new SQLException("发生异常了..");
}
return rows;
}

原因:Spring 默认的事务规则是遇到运行异常 RuntimeException 和程序错误 Error 才会回滚。如果想针对检查异常进行事务回滚,可以在 @Transactional 注解里使用 rollbackFor 属性明确指定异常

1
@Transactional(rollbackFor = Exception.class)

业务层捕捉异常,事务不生效

在业务层手工捕捉并处理了异常(try…catch),Spring 自然不知道这里有错,更不会主动去回滚数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Transactional
public int insert(User user) throws Exception {

int rows = insertUser(user);
insertUserRole(user);
// 模拟抛出 SQLException 异常
boolean flag = true;
if (flag) {
try {
// 谨慎:尽量不要在业务层捕捉异常并处理
throw new SQLException("发生异常了..");
} catch (Exception e) {
e.printStackTrace();
}
}
return rows;
}

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

附录

Transactional 注解的常用属性表

属性说明
propagation事务的传播行为,默认值为 REQUIRED
isolation事务的隔离度,默认值采用 DEFAULT
timeout事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务
read-only指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true
rollbackFor用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……}
noRollbackFor抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……}
….

事务的传播机制是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个 @Transactinal 注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。

TransactionDefinition 传播行为的常量

常量含义
TransactionDefinition.PROPAGATION_REQUIRED如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值
TransactionDefinition.PROPAGATION_REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起
TransactionDefinition.PROPAGATION_SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
TransactionDefinition.PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起
TransactionDefinition.PROPAGATION_NEVER以非事务方式运行,如果当前存在事务,则抛出异常
TransactionDefinition.PROPAGATION_MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
TransactionDefinition.PROPAGATION_NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
点击查看

本文标题:SpringBoot 使用 @Transactional 无效

文章作者:北宸

发布时间:2021年09月25日 - 17:35:01

最后更新:2023年08月19日 - 13:26:00

原始链接:https://www.liaofuzhan.com/posts/1059904857.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------------本文结束 感谢您的阅读-------------------
🌞