多数据源事务控制解决方案(分布式事务控制)

利用spring boot集成JTA(Atomikos案例)实现分布式事务控制;

maven引入:


    org.springframework.boot
    spring-boot-starter-jta-atomikos

原理简述:

将spring原有的数据源信息管理的类型改为atomikos实现的AtomikosDataSourceBean数据源类(他也是javax.sql.DataSource的实现类),该类有一个属性是xaDataSource(xa就是分布式事务处理的一种模型规范),实现一下xaDataSource赋给这个属性;

之后各个数据源不要再独自去声明事务控制对象了,因为这时候Atomikos会自动有一个统一的分布式事务控制对象来控制事务;

代码:

application.yml 数据源配置方式:

spring:
  datasource01:
    mapperPackage: com.alicyu.springcloud.dao.dbone         #mapper包路径
    mapperxmlDir: classpath:mybatis/mapper/dbone/**/*.xml      #mapper.xml路径
    entityPackage: com.alicyu.springcloud.entities.dbone    #实体包路径
    mybatiscfg: classpath:mybatis/mybatis.cfg.xml      #mapper对应mybatis通用配置文件路径
    url: jdbc:mysql://localhost:3306/clouddb01?useSSL=false
    username: root
    password: zhicheng
    minPoolSize: 3
    maxPoolSize: 25
    maxLifetime: 20000
    borrowConnectionTimeout: 30
    loginTimeout: 30
    maintenanceInterval: 60
    maxIdleTime: 60
  datasource02:
    mapperPackage: com.alicyu.springcloud.dao.dbtwo         #mapper包路径
    mapperxmlDir: classpath:mybatis/mapper/dbtwo/**/*.xml      #mapper.xml路径
    entityPackage: com.alicyu.springcloud.entities.dbtwo    #实体包路径
    mybatiscfg: classpath:mybatis/mybatis.cfg.xml      #mapper对应mybatis通用配置文件路径
    url: jdbc:mysql://localhost:3306/clouddb02?useSSL=false
    username: root
    password: zhicheng
    minPoolSize: 3
    maxPoolSize: 25
    maxLifetime: 20000
    borrowConnectionTimeout: 30
    loginTimeout: 30
    maintenanceInterval: 60
    maxIdleTime: 60
package com.alicyu.config;

import lombok.Data;

@Data
public class DBConfig1 {
   private String entityPackage;
   private String mapperxmlDir;
   private String mybatiscfg;
   private String url;
   private String username;
   private String password;
   private int minPoolSize;
   private int maxPoolSize;
   private int maxLifetime;
   private int borrowConnectionTimeout;
   private int loginTimeout;
   private int maintenanceInterval;
   private int maxIdleTime;
   private String testQuery;
}
package com.alicyu.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
public class DBConfig2 {
   private String entityPackage;
   private String mapperxmlDir;
   private String mybatiscfg;
   private String url;
   private String username;
   private String password;
   private int minPoolSize;
   private int maxPoolSize;
   private int maxLifetime;
   private int borrowConnectionTimeout;
   private int loginTimeout;
   private int maintenanceInterval;
   private int maxIdleTime;
   private String testQuery;
}
package com.alicyu.config;

import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
@MapperScan(basePackages = "${spring.datasource01.mapperPackage}", sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceOneConfig {
   
    @Bean(name = "DBConfig1")
    @ConfigurationProperties(prefix="spring.datasource01")
    @Primary
    public DBConfig1 dBConfig1() {
        return new DBConfig1();
    }

    @Bean(name = "DataSource")
    @Primary
    public DataSource dataSource(@Qualifier("DBConfig1")DBConfig1 testConfig) throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        // 将本地事务注册到创 Atomikos全局事务
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("DataSource");
        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }


    //提供SqlSeesion
    @Bean(name = "SqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("DataSource") DataSource dataSource,@Qualifier("DBConfig1")DBConfig1 testConfig) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        //下边的值可以通过@value
        // 实体返回映射
        sqlSessionFactoryBean.setTypeAliasesPackage(testConfig.getEntityPackage());
        // sql xml文件路径
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(testConfig.getMapperxmlDir()));
        // 配置文件
        sqlSessionFactoryBean.setConfigLocation(resolver.getResource(testConfig.getMybatiscfg()));
        return sqlSessionFactoryBean.getObject();
    }

//    因为事务会统一交给Atomikos全局事务,(因为是用了AtomikosDataSourceBean管理数据源),所以不能添加其他事务管理器
//    // 事务管理
//    @Bean(name = "transactionManager")
//    @Primary
// public DataSourceTransactionManager transactionManager(@Qualifier("DataSource") DataSource dataSource) {
//    return new DataSourceTransactionManager(dataSource);
// }
    // sqlSessionTemplate
    @Bean(name = "sqlSessionTemplate")
    @Primary
   public SqlSessionTemplate sqlSessionTemplate(@Qualifier("SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
   }

}
package com.alicyu.config;

import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
@MapperScan(basePackages = "${spring.datasource02.mapperPackage}", sqlSessionFactoryRef = "SqlSessionFactory02")
public class DataSourceTwoConfig {
   
    @Bean(name = "DBConfig2")
    @ConfigurationProperties(prefix="spring.datasource02")
    public DBConfig2 dBConfig2() {
        return new DBConfig2();
    }

    @Bean(name = "DataSource02")
    public DataSource dataSource(@Qualifier("DBConfig2")DBConfig2 testConfig) throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        // 将本地事务注册到创 Atomikos全局事务
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("DataSource02");
        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }

    //提供SqlSeesion
    @Bean(name = "SqlSessionFactory02")
    public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("DataSource02") DataSource dataSource,@Qualifier("DBConfig2")DBConfig2 testConfig) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        //下边的值可以通过@value
        // 实体返回映射
        sqlSessionFactoryBean.setTypeAliasesPackage(testConfig.getEntityPackage());
        // sql xml文件路径
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(testConfig.getMapperxmlDir()));
        // 配置文件
        sqlSessionFactoryBean.setConfigLocation(resolver.getResource(testConfig.getMybatiscfg()));
        return sqlSessionFactoryBean.getObject();
    }
    //    因为事务会统一交给Atomikos全局事务,(因为是用了AtomikosDataSourceBean管理数据源),所以不能添加其他事务管理器
//    // 事务管理
//    @Bean(name = "transactionManager02")
// public DataSourceTransactionManager transactionManager(@Qualifier("DataSource02") DataSource dataSource) {
//    return new DataSourceTransactionManager(dataSource);
// }
    // sqlSessionTemplate
    @Bean(name = "sqlSessionTemplate02")
   public SqlSessionTemplate sqlSessionTemplate(@Qualifier("SqlSessionFactory02") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
   }

}

 

之后对两个数据源的操作,抛异常后数据都会滚了

多数据源事务控制解决方案(分布式事务控制)_第1张图片

有时候还会报一些分布式事务授权问题,有时候及时报这个错也能用,然后要给相应的数据库用户受相应的权限

解决方法:数据库执行 GRANT XA_RECOVER_ADMIN ON *.* TO `root`@`%`; 授予root用户在所有库下的所有表上执行 XA RECOVER语句的权限     或参考:https://ask.csdn.net/questions/759904

你可能感兴趣的:(多数据源事务控制解决方案(分布式事务控制))