一篇文章学会使用@Transactional
@Transactional
参考文档
https://docs.spring.io/spring-framework/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#transaction-declarative-annotations
作用范围
-
方法
当类配置了
@Transactional
,方法也配置了@Transactional
,方法的事务会覆盖类的事务属性信息。 -
类
当把
@Transactional
注解作用在类上面时,表示该类的所有public方法都配置相同的事务属性信息。 -
接口
不推荐配置在接口,因为一旦配置配置在
Interface
上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional
注解失效
注解属性有哪些
-
Propagation 事务的传播属性
-
REQUIRED 默认属性
支持当前事务,如果存在就加入当前事务,如果当前不存在事务就创建一个新事务
如果A方法和B方法都添加了注解,且在默认事务传播行为下,两个事务会合并成一个事务
-
SUPPORTS
如果当前存在事务,则加入该事务。如果当前不存在事务,在以非事务的方式运行
-
MANDATORY
如果当前存在事务,则加入该事务。如果当前不存在事务,在抛出异常
-
REQUIRES_NEW
开启一个新事务,如果当前存在事务,则暂停当前事务
类A中的a方法使用默认的Propagation.REQUIRED模式,类B中的b方法加上Propagation.REQUIRED_NEW模式,然后在a方法中调用b方法操作数据库,a方法发生异常后, b方法不会回滚。因为b方法的Propagation.REQUIRED_NEW模式会暂停a方法的事务
-
NOT_SUPPORTED
以非事务的方式运行,如果当前存在事务,暂停当前的事务
-
NEVER
以非事务的方式运行,如果当前存在事务,则抛出异常
-
NESTED
如果当前存在事务,则在嵌套事务中执行。效果与REQUIRED类似
-
-
isolation 事务的隔离级别,默认值
-
DEEAULT
使用底层数据库的默认隔离级别
-
READ_UNCOMMITED
读未提交,会有脏读,不可重复读,幻象读发生。由一个事务更改的行可以在未提交前被另一个事务读取(脏读),如果事务被回滚,则第二个事务将检索到无效的行
-
READ_COMMITED
读提交,解决脏读,但是还会有不可重复读,幻读现象发生。仅允许读取已提交事务的信息
-
REPEATABLE_READ
可重复读,可以防止脏读和不可重复读,但是幻象读还会发生。可以保证同一个事务多次读取数据都是一致的
-
SERIALIZABLE
串行化,最高级别,事务一个一个的执行,效率最低,隔离级别最高,能够解决脏读,幻读,不可重复读
-
-
timeout 事务的超时时间
默认值-1,超过该时间限制,但是事务还没有完成,则自动回滚事务
-
readOnly 只读事务
默认false,对于不需要事务的方法可以设置为true
-
rollbackFor
定义一个或者多个异常回滚的类型,它们必须是Throwable的子类,默认会在RuntimeException和Error上回滚,对于非检查异常不会进行回滚
-
rollbackForClassName
与rollbackFor类似,只是值为字符串类型的异常类名称。例如值为ServletException则会匹配javax.servlet.ServletException
-
noRollbackFor
指定一个或者多个异常不进行异常回滚,值与rollbackFor类型一样,作用相反
-
noRollbackForClassName
指定一个或者多个不进行异常回滚操作,值与rollbackForClassName类型一样,作用相反
注解失效的场景有哪些
-
注解应用在非public修饰的方法上
需要注意的一点是注解在private,protected方法上事务无效,但是不会报错
AbstractFallbackTransactionAttributeSource.computeTransactionAttribute // 方法会检查是否是public方法, // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; }
-
@Transactional 注解属性propagation 设置错误
- SUPPORTS:如果当前存在事务就加入该事务,如果当前不存在事务再以非事务的方式运行
- NOT_SUPPORTS: 以非事务的方式运行,如果当前存在事务,暂停当前事务
- NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常
-
@Transactional 注解属性rollBackFor设置错误
rollBackFor指定触发事务回滚的异常类型,默认是RuntimeException和Error会触发回滚,其它的异常不会出发回滚事务,如果希望自定义的异常出发回滚需要指定自定义的异常类型
@Transactional(rollbackFor= MyException.class) // 源码如下 public int getDepth(Throwable ex) { return getDepth(ex.getClass(), 0); } private int getDepth(Class<?> exceptionClass, int depth) { if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... if (exceptionClass == Throwable.class) { return -1; } return getDepth(exceptionClass.getSuperclass(), depth + 1); }
-
同类方法调用
此时方法B是不会触发事务回滚的,因为事务的方法只有被当前类之外的方法调用时才会有Spring的代理对象管理
public class Test{ public void A(){ B(); } @Transactional public void B() throw Exception{ // insert } }
-
异常被catch吃掉
public void A(){ try{ B(); }catch(Exception e){ log.error(e); } } @Transactional public void B(){ // insert }
-
数据库引擎本身不支持事务
MySQL 本身已经默认InnoDB引擎,是支持事务的,MyISam不支持事务,现在用的基本也没有了,一旦数据库使用不支持事务的引擎,那注解从根本上也就不管了
测试事务是否生效记录
测试方法为类A方法A调用类B方法B,携带注解为@Transactionsal(rollbackFor=Exception.class)
测试代码结构,通过改变异常位置,注解位置测试
public class A{
@Autowired
private B b;
public void A() {
b.B();
}
}
public class B{
@Autowired
private TestSysMapper testSysMapper;
@Transactional(rollbackFor = Exception.class)
public void B() {
TestSys testSys = new TestSys();
testSys.setId(1);
testSys.setName("name1");
testSysMapper.insert(testSys);
if (true){
throw new RuntimeException();
}
}
}
测试说明: 数据插入说明事务未生效,未插入数据说明事务成功
默认测试注解形式为:@Transactional(rollbackFor = Exception.class)
A方法是否带注解 | B方法是否带注解 | 异常位置 | 数据是否插入 |
---|---|---|---|
带 | 不带 | 类B方法B | false |
不带 | 带 | 类B方法B | false |
不带 | 带 | 类A方法A | true |
带 | 不带 | 类A方法A | false |
带@Transactional(rollbackFor = Exception.class) | 带@Transactional(rollbackFor = Exception.class) | 类A方法A | false |
带@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS) | 类A方法A | false |
带@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY) | 类A方法A | false |
带@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW) | 类A方法A | true |
带@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED) | 类A方法A | True |
带@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class,propagation = Propagation.NEVER) | 类A方法A | false |
带@Transactional(rollbackFor = Exception.class) | @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED) | 类A方法A | false |
感觉有帮助可以关注一下公众号获取最新文章输出
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南