一篇文章学会使用@Transactional

本文详细介绍了Spring框架中@Transactional注解的使用,包括作用范围、注解属性如Propagation、isolation等,以及事务失效的场景。讨论了如何配置事务传播行为以避免事务合并或异常处理不当导致的回滚问题,并提供了测试事务是否生效的案例分析。
摘要由CSDN通过智能技术生成

@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方法Bfalse
不带类B方法Bfalse
不带类A方法Atrue
不带类A方法Afalse
带@Transactional(rollbackFor = Exception.class)带@Transactional(rollbackFor = Exception.class)类A方法Afalse
带@Transactional(rollbackFor = Exception.class)@Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)类A方法Afalse
带@Transactional(rollbackFor = Exception.class)@Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)类A方法Afalse
带@Transactional(rollbackFor = Exception.class)@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)类A方法Atrue
带@Transactional(rollbackFor = Exception.class)@Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)类A方法ATrue
带@Transactional(rollbackFor = Exception.class)@Transactional(rollbackFor = Exception.class,propagation = Propagation.NEVER)类A方法Afalse
带@Transactional(rollbackFor = Exception.class)@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)类A方法Afalse

感觉有帮助可以关注一下公众号获取最新文章输出
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值