Spring 事务

时间:2024-03-09 20:05:55

1、事务分类

1、编程式事务

基于底层的API,如PlatformTransactionManager、TransactionDefinition 和 TransactionTemplate 等核心接口

@Autowired
private DataSourceTransactionManager transactionManager;

public void test() {
      TransactionDefinition def = new DefaultTransactionDefinition();
      TransactionStatus status = transactionManager.getTransaction(def);

       try {
         // TODO 事务操作

         // 事务提交
         transactionManager.commit(status);
      } catch (DataAccessException e) {
         // 事务回滚
         transactionManager.rollback(status);
         throw e;
      }
}

或者使用spring 的bean对象TransactionTemplate 实现

  @Autowired
    private TransactionTemplate transactionTemplate;

  	public void test(){
      return transactionTemplate.execute(transactionStatus -> {
         //事务操作
      });
    }

	public void test1(){
      transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          @Override
          protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
              
          }
      });
    }

2、声明式事务

通过@Transactional 注解,本质上通过AOP方式实。仅限声明在方法上

@Transactional
public void test() {
     // 事务操作  
}

事务不生效场景

  • 1、声明在非public 方法或static方法上
  • 2、同一个class文件,方法内部调用
  • 3、不是通过bean对象 方法的方式调用
  • 4、抛出的异常与rollbackFor不一致
  • 5、方法内部手动catch异常,并未抛出
  • 6、数据库未开启或不支持事务
  • 7、属性 propagation 设置错误

局限性

1、RPC远程调用,本地方法回滚,但是rpc远程方法无法回滚

2、方法上的声明,粒度大,无法针对具体的代码块,必须提炼成新方法

3、远程调用,导致事务长时间锁定

4、出现在分库分表、跨库查询中,无法回滚

5、AOP切面间相互影响,比如存在全局异常捕获的切面

[参考]@Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。

3、多线程下的事务生效问题

1、基于注解的@Transcational 在多线程下不生效,由于aop切面维护了一个ThreadLocal存储事务的上下文信息。

2、基于编程式,在多线程下生效需要开启线程间数据共享

  • 1、 通过传递参数的方式,在创建子线程传递。
  • 2、父线程创建 InheritableThreadLocal 静态的类变量,子线程可以自动继承父线程的threadlocal,从而达成共享