Spring Transaction

Spring Transaction

什么是事务?

事务是一组关联的数据库操作,要么全部成功执行,要么全部回滚,保障了数据库操作的一致性、完整性。Spring本身没有事务,是通过集成和调用持久层实现的事务。

事务的分类

  • 编程式事务

    • 优点:粒度更小,可以在try-catch代码块锁定精确的范围

    • 缺点:配置繁琐、代码耦合度高

      1
      2
      3
      4
      5
      6
      7
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource" />
      </bean>

      <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
      <property name="transactionManager" ref="transactionManager" />
      </bean>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      transactionTemplate.execute(new TransactionCallback<Object>() {
      @Override
      public Object doInTransaction(TransactionStatus status) {
      try {
      // 在此处执行事务操作
      // 例如,插入、更新、删除等数据库操作

      // 如果发生异常,事务会被回滚
      // 如果一切正常,事务会被提交
      } catch (Exception e) {
      // 捕捉异常,标记事务为回滚状态
      status.setRollbackOnly();
      e.printStackTrace();
      }
      return null;
      }
      });
  • 声明式事务

    • 优点:降低耦合、减少对代码的侵入性配置简单
    • 缺点:@Transactional是方法级的、粒度更大

    需要在配置类/文件中配置好 DataSourceTansactionManager 然后添加 @EnableTransactionManagement ,就可以开始使用@Transactonal注解了。

问题:怎么缩小声明式事务的粒度?

将使用事务的方法拆分成小的方法,在整个方法里面调用小方法,在小方法上添加@Transactional

事务的实现原理

Spring事务是基于AOP实现的,

对一个方法开启事务对应着用AOP将这个方法增强,使用一个around advice 首先在前置通知事务管理器在方法调用前开启事务,然后执行方法,然后在返回后通知提交事务,或者在抛出后通知回滚事务,然后再在后置通知关闭事务,rollbackfor对应着AOP的catch代码块捕获的异常。这样理解对吗?

@Transactional注解

  • @Transactional是spring声明式事务的核心注解;

  • 它可以被添加到方法上,起到事务的作用(事务的声明是方法级的);

  • 添加到的方法如果有unchecked的(没有被catch的)RuntimeException及其子类异常,将会触发事务回滚。(编译期异常不会触发)

  • 如果方法里面检查了异常比如添加了try-catch代码块,将不会触发事务回滚,如果让事务能够正常回滚,则需要在catch内添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

  • @Transaction的属性

    • isolation 指定当前事务的隔离级别,默认是数据库的默认隔离级别

    • **propagation **指定传播行为

    • timeOut 指定事务的超时时间,单位是秒,默认值-1(没有超时时间)

    • **readOnly **默认是false,声明事务里面有读有写,改成true时,告诉数据库事务中只有读,可以做优化,提高查询效率

    • rollbackFor 指定哪些异常触发回滚

    • rollbackForClass

    • noRollbackFor

    • noRollbackForClass

事务的传播行为

事务的传播行为指的是在一个声明了事务的方法中,调用了另外一个声明了事务的方法(嵌套事务),这个时候需要设置事务的传播行为,定义在方法调用链中嵌套事务如何相互影响。

image-20230827112927367

事务的传播行为由propagation属性声明

事务传播行为的分类

  • REQUIRED 方法调用时,如果调用者已经存在事务,就加入到调用者的事务,如果不存在事务,就开启一个新的事务(使用被调用者的事务)

    image-20230827114106992
  • REQUIRES_NEW 方法调用时,将调用者的事务挂起,开启一个新的事务(使用被调用者的事务),等待新事务返回后继续原来的事务。

    image-20230827114443130
  • NESTED

    开启嵌套的两个事务,调用者的事务回滚会影响被调用者,被调用者的回滚不会影响调用者的事务。

    image-20230827114654851
  • NEVER

    不允许有别的事务,否则抛异常

  • image-20230827133700088

思考:propagation属性声明在哪个位置?调用者声明还是被调用者?

被调用者,被调用者根据传播行为来选择自己使用事务的方式。

事务为什么会失效

  • 没有正确配置事务、没有开启事务、没有@Transactional注解
  • 加了@Transactional但是方法调用中使用了this,绕开了代理。?

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!