基于boot实现的单例多库分布式事务

Cheetahcubs 2020-01-03

场景描述:

       业务系统中存在针对用户对一些特定字段(如:身份证、银行卡号)的操作,需要进行日志记录及入库日志。项目架构是基于boot为基石的SpringCloud分布式架构,业务模块暂时称呼为模块A,日志记录属于公共模块暂时称呼为模块B。模块A操作步骤成功之后,会调用模块B进行日志记录。

名词解释:

       数据库事务的ACID (原子性、一致性、隔离性、持久性),分布式事务的BASE理论(分布式事务要求实现最终一致性)。

实现核心技术:

       Jta+atomikos

jta:java中对事务处理的api

atomikos:为Java平台提供增值服务的并且开源类事务管理器

实现步骤:

1、在pom文件文件中添加依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jta-atomikos</artifactId>
 </dependency>

2、在application.properties中配置两个数据源的

spring:
  datasource:
    # 数据库1
    db1:
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      jdbc-url: jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false&allowMultiQueries=true
      username: root
      password: newpwd

    # 数据库2
    db2:
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
      jdbc-url: jdbc:mysql://localhost:3306/test2?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false&allowMultiQueries=true
      username: root
      password: newpwd

  # 分布式锁
  jta:
    # log-dir: classpath*:tx-logs
    transaction-manager-id: txManager

3、添加conf配置类:

@Data
@ConfigurationProperties(prefix = "spring.datasource.db1")
public class DB1Config {

    @Value("${spring.datasource.db1.jdbc-url}")
    private String url_jdbc;

    @Value("${spring.datasource.db1.username}")
    private String username;

    @Value("${spring.datasource.db1.password}")
    private String password;
}

4、添加数据源类:

@Configuration
@MapperScan(basePackages = "com.example.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
public class DB1DataSourcesConfig {

    @Primary
    @Bean(name = "db1DataSource")
    public DataSource dataSource(DB1Config DB1Config) {
        MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
        mysqlXADataSource.setUrl(DB1Config.getUrl_jdbc());
        mysqlXADataSource.setUser(DB1Config.getUsername());
        mysqlXADataSource.setPassword(DB1Config.getPassword());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);

        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("db1DataSource");

        return atomikosDataSourceBean;
    }

    @Primary
    @Bean(name = "db1SqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/db1/*.xml"));

        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);

        sessionFactoryBean.setConfiguration(configuration);
        return sessionFactoryBean.getObject();
    }

    @Primary
    @Bean(name = "db1SqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

5、添加db2的配置类和数据源类,差别与db1的地方在于:db1是默认的数据源,有@Primary注解,db2的不需要。

6、在启动类上添加注解,指定扫描路径:

@EnableConfigurationProperties(value = {DB2Config.class, DB1Config.class})

7、在要进行事务控制的方法上添加@Transaction注解,通过调用dao层的方法,实现对数据库的操作。(此处可以用jpa或者mybatis)

总结:

SpringBoot 通过在启动时加载不同的数据源,并将不同的数据源注入到不同的 repository 包下,从而实现项目多数据源操作,在项目中使用多数据源时,需要用到哪个数据源,只需要将对应包下的 repository 注入操作即可。

相关推荐