@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 |
感觉有帮助可以关注一下公众号获取最新文章输出