Spring多数据源事务控制

371 阅读2分钟

编程式事务控制

注入transactionManager

@Bean
public DataSourceTransactionManager rTransactionManager(){
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(rDataSource());
    return dataSourceTransactionManager;
}

注入transactionTemplate

@Bean
    public TransactionTemplate rTransactionTemplate(){
        return new TransactionTemplate(rTransactionManager());
    }

开始事务控制

@EnableTransactionManagement  // 开启事务

image.png

具体使用

@Autowired
TransactionTemplate rTransactionTemplate;

@Autowired
TransactionTemplate wTransactionTemplate;

@Override
public void saveAll(Frend frend) {

    wTransactionTemplate.execute(wstatus->{
        rTransactionTemplate.execute(rstatus->{
            try{
                wFrendMapper.save(frend);
                rFrendMapper.save(frend);
                int i=1/0;
                //r库提交
               return  true;
            }catch (Exception e){
                e.printStackTrace();
                wstatus.setRollbackOnly(); //注意此处是set 回滚方法
                rstatus.setRollbackOnly();
                return  false;
            }
        });
        // 主库执行成功,提交。
           return true;
    });
}

声明式事务控制

开启事务注解

@EnableTransactionManagement // 开启事务

具体使用

  
  
/**
 * 2. Spring中七种Propagation类的事务属性详解:     *
 *  REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。     *
 *  SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。     *
 *  MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。     *
 *  REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。     *
 *  NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。     *
 *  NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。     *
 *  NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
 * ————————————————
 * spring 事务:https://blog.csdn.net/sayoko06/article/details/79164858    
 */
 @Transactional(transactionManager = "wTransactionManager")
  @Override
  public void saveAll(Frend frend) {
  // 此处是获取当前类的代理类,如果使用this.saveAllR()则会导致rTransactionManager事务没有没被spring管理,会导致从库事务回滚异常。
      FrendService frendService = (FrendService) AopContext.currentProxy();
      frendService.saveAllR(frend);

  }
 
@Transactional(transactionManager = "rTransactionManager",
        propagation = Propagation.REQUIRES_NEW) //新建事务,如果当前存在事务,把当前事务挂起。
@Override
public void saveAllR(Frend frend) {
    wFrendMapper.save(frend);
    rFrendMapper.save(frend);
    int i = 1 / 0;
    return;
}

框架事务控制

使用【dynamic-datasource】框架进行事务控制

引入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.0</version>
</dependency>

数据源配置

spring:
  datasource:
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为master
      primary: mysql
      #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        mysql:
          url: jdbc:mysql://127.0.0.1:3306/shiguang?serverTimezone=CST&useSSL=false&rewriteBatchedStatements=true
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
          hikari:
            ## 最小空闲连接数
            minimum-idle: 5
            #      ## 最大连接数
            #      maximum-pool-size: 20
            ## 自动提交
            auto-commit: true
            ## 连接池名称
            pool-name: TmcHikariCP
            ## 超时时间(ms)
            connection-timeout: 30000
            ## 超时测试语句
            connection-test-query: select now()

        sqlServer:
          url:  jdbc:sqlserver://127.0.0.1:1433;databasename=bqwms
          username: sa
          password: 1qaz@WSX
          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver

具体使用

@Override
@DS("master")
public void save(Frend frend) {
    frendMapper.save(frend);
}


@DS("master")
@DSTransactional
public void saveAll(){
    // 执行多数据源的操作
}

原理解析

  • 1.通过DynamicDataSourceAutoConfiguration自动配置类,
    1. 配置了DynamicRoutingDataSource 它就相当于我们之前自定义的那个DynamicDataSource, 用来动态提供数据源
    1. 配置DynamicDataSourceAnnotationAdvisor 就相当于 之前 自定义的一个切面类
  • 4.设置DynamicDataSourceAnnotationInterceptor 当前advisor的拦截器 , 把它理解成之前环绕通知
    1. 当执行方法会调用DynamicDataSourceAnnotationInterceptor#invoke 来进行增强: image.png
    1. 在执行数据库操作时候, 就会调用DataSource.getConnection, 此时的DataSource指的就是DynamicRoutingDataSource
    1. 然后执行模板方法

image.png