SpringBoot整合多数据源,atomikos 解决分布式事务报错

 

背景:SpringBoot整合多数据源及分布式事物管理

 

1. SpringBoot分布式事物管理:使用springboot+jta+atomikos 分布式事物管理

SpringBoot 父类版本为2.1.2






    org.springframework.boot
    spring-boot-starter-parent
    2.1.2.RELEASE

 

2. 新增 jta-atomikos依赖


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

 

3. 新增application.yml配置文件,数据库版本是mysql_8.0.16

#端口号
server:
  port: 8026

# 数据库连接
# &serverTimezone=GMT%2B8 为中国东八区标准时区
spring:
  datasource:
    #datasource1
    test01:
      #jdbc-url: jdbc:mysql://127.0.0.1:3306/test01?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
      url: jdbc:mysql://127.0.0.1:3306/test01?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

      minPoolSize: 3
      maxPoolSize: 25
      maxLifetime: 20000
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60

    ###datasource2
    test02:
      #jdbc-url: jdbc:mysql://127.0.0.1:3306/test02?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
      url: jdbc:mysql://127.0.0.1:3306/test02?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

      minPoolSize: 3
      maxPoolSize: 25
      maxLifetime: 20000
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60

 

4. 新增配置文件实体类

4.1 第一个实体类

package com.aop.config;

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

/**
 * @Author : richard_aop.
 * @Date :   2019-08-16
 * @Data是集成了lombok 插件,相当于@Getter和@Setter
 * 自动生成get和set方法与toString方法
 */

@Data
@ConfigurationProperties(prefix = "spring.datasource.test01")
public class DBConfig1 {

    private String url;
    private String username;
    private String password;
    private String driverClassName;

    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;

}

4.2 第二个实体类

@Data
@ConfigurationProperties(prefix = "spring.datasource.test02")
public class DBConfig2 {

    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;

}

 

5. 多数据源事务处理

5.1  DataSourceConfig1

package com.aop.datasource;

import com.aop.config.DBConfig1;
import com.mysql.cj.jdbc.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @Author : richard_aop.
 * @Date :  2019-08-16
 * 读取DataSource1数据源
 */

@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.aop.test01.mapper", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class MyBatisConfig1 {

    // 配置数据源
    @Bean(name = "test1DataSource")
    public DataSource testDataSource(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("test1DataSource");

        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;
    }

    @Bean(name = "test1SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean(name = "test1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

5.2  DataSourceConfig2

// TODO @MapperScan(basePackages = "") 扫包范围必须制定到.mapper不然报错无效绑定声明
@Configuration
@MapperScan(basePackages = "com.aop.test02.mapper", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {

    // 配置数据源
    @Primary
    @Bean(name = "test2DataSource")
    public DataSource testDataSource(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("test2DataSource");

        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;
    }

    @Primary
    @Bean(name = "test2SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Primary
    @Bean(name = "test2SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

 

6. 按图示写方法与启动类,启动类加载配置文件信息

SpringBoot整合多数据源,atomikos 解决分布式事务报错_第1张图片 SpringBoot整合事物项目结构
package com.aop;

import com.aop.config.DBConfig1;
import com.aop.config.DBConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

/**
 * @Author : richard_aop.
 * @Date :  2019-08-16
 */


@SpringBootApplication
// 开启读取配置文件
@EnableConfigurationProperties(value = {DBConfig1.class, DBConfig2.class})
public class MyBatisApp {

    //private static final Logger logger = LoggerFactory.getLogger(MyBatisApp.class);

    public static void main(String[] args) {
        SpringApplication.run(MyBatisApp.class, args);
        System.out.println("SpringBoot整合多数据源!");
    }

}

 

7.  以上配置文件配置好之后,项目启动报错如下:

java.sql.SQLException: XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency

项目构建的时候的 JTA 依赖与插件都已经ok,项目还是报错。

Google和百度之后:

改正如下

当前访问mysql的账号缺少系统权限,我的是root(即admin)账户缺少 “允许执行 XA RECOVER语句”的权限:XA_RECOVER_ADMIN,以root账号访问mysql,执行“GRANT XA_RECOVER_ADMIN ON *.* TO root@'%';”语句,当然账号和IP需要根据自己的来改,此处为了节省时间直接改为*.*。

现在我的root(即admin)账户权限------>
mysql> SHOW GRANTS FOR root@'%';
+----------------------------------------------------------------+
| Grants for root@% |
+----------------------------------------------------------------+
| GRANT USAGE ON . TO root@% |
| GRANT XA_RECOVER_ADMIN ON . TO root@% |
| GRANT ALL PRIVILEGES ON snail_slow_delivery.* TO root@% |
+----------------------------------------------------------------+

 

 

以上内容如有不妥之处,请各位详细指正并留言,便于今后共同成长!

在此也希望这篇博客能够对大家有所帮助!

 

 

你可能感兴趣的:(SpringBoot)