Springboot 2.X——jta-atomikos+mybatis多数据源配置和事务管理

一、简介

一般的企业做一个项目,一般也就只会用到一个数据库,一个数据源就可以了。但是考虑到分库操作后,需要对多个数据库、数据表进行CRUD操作,此时则需要在一个服务层操作数据时,必须保证全局事务能够正常进行。

二、配置

整体的项目布局:

Springboot 2.X——jta-atomikos+mybatis多数据源配置和事务管理_第1张图片

主要的配置方式:

1、依赖引入

在一般的springboot+mybatis配置中,只需要额外添加pom依赖


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

2、yml数据源参数配置

server:
  port: 80

mysql:
  datasource:
    test1: 
      url: jdbc:mysql://localhost:3306/banana1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root
      minPoolSize: 3
      maxPoolSize: 25
      maxLifetime: 20000
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      test-query: select 1
    test2: 
      url: jdbc:mysql://localhost:3306/banana2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root
      minPoolSize: 3
      maxPoolSize: 25
      maxLifetime: 20000
      borrowConnectionTimeout: 30
      loginTimeout: 30
      maintenanceInterval: 60
      maxIdleTime: 60
      test-query: select 1

3、数据源的配置文件编写

主数据源配置,必须在配置的项中添加 @Primary 注解,其次无需配置事务,多个数据源的事务,交由 jta 统一处理。

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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 org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.alibaba.druid.pool.xa.DruidXADataSource;

import cn.linkpower.config.po.DBConfig1;

/**
 * 第一个数据源配置 --- 不配置事务
 * @author 76519
 *
 */
@Configuration
@MapperScan(basePackages="cn.linkpower.dao.mapper1",sqlSessionFactoryRef="sqlSessionFactory1")
public class DataSourceConfig1 {
	
	@Bean(name="dataSource1")
    @Primary
    public DataSource dataSource1(DBConfig1 dbConfig1) throws Exception {
        DruidXADataSource druidXADataSource = new DruidXADataSource();
        druidXADataSource.setUrl(dbConfig1.getUrl());
        druidXADataSource.setUsername(dbConfig1.getUsername());
        druidXADataSource.setPassword(dbConfig1.getPassword());
         
        //使用AtomikosDataSourceBean封装com.alibaba.druid.pool.xa.DruidXADataSource
        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(druidXADataSource);
        //atomikos要求为每个AtomikosDataSourceBean编辑名称
        atomikosDataSourceBean.setUniqueResourceName("dataSource1");
        atomikosDataSourceBean.setPoolSize(5);
        return atomikosDataSourceBean;
    }
	
	@Bean(name = "sqlSessionFactory1")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("dataSource1") DataSource masterDataSource)
        throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/bunana1/*Mapper.xml"));
        return sessionFactory.getObject();
    }
}

从数据源配置,和主数据源配置一样,不需要配置事务,事务交由 jta 同一处理。

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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 org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.alibaba.druid.pool.xa.DruidXADataSource;

import cn.linkpower.config.po.DBConfig2;

/**
 * 第二个数据源配置 --- 不配置事务
 * @author 76519
 *
 */
@Configuration
@MapperScan(basePackages="cn.linkpower.dao.mapper2",sqlSessionFactoryRef="sqlSessionFactory2")
public class DataSourceConfig2 {
	
	@Bean(name="dataSource2")
    public DataSource dataSource1(DBConfig2 dbConfig2) throws Exception {
        DruidXADataSource druidXADataSource = new DruidXADataSource();
        druidXADataSource.setUrl(dbConfig2.getUrl());
        druidXADataSource.setUsername(dbConfig2.getUsername());
        druidXADataSource.setPassword(dbConfig2.getPassword());
 
        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(druidXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("dataSource2");
        atomikosDataSourceBean.setPoolSize(5);
        
        return atomikosDataSourceBean;
    }
	
	@Bean(name = "sqlSessionFactory2")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("dataSource2") DataSource masterDataSource)
        throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/bunana1/*Mapper.xml"));
        return sessionFactory.getObject();
    }
}

4、到此我们发现我分别引入了 DBConfig1.class 和 DBConfig2.class 这两个文件是什么呢?

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;

@Data
@Component 
@ConfigurationProperties(prefix="mysql.datasource.test1")
public class DBConfig1 {
	private String driverClassName;
    private String url;
    private String username;
    private String password;
}

此处只写明了 DBConfig1.java 类的属性,同时使用了 Lombok 插件简化代码。

注意:

配置类文件要想能够使用 application.yml 或者 application.properties 文件中的配置信息,必须使用 @Component 注解注释,否则会出现

Parameter 0 of method dataSource1 in cn.linkpower.config.DataSourceConfig1 required a bean of type 'cn.linkpower.config.po.DBConfig1' that could not be found

类似的报错信息。

5、使用 jta 配置全局事务管理

import javax.transaction.UserTransaction;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.jta.JtaTransactionManager;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;

/**
 * 整体事务配置
 * @author 76519
 *
 */
@Configuration
public class TXManagerConfig {
	@Bean(name = "transactionManager")
    @Primary
    public JtaTransactionManager regTransactionManager () {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        UserTransaction userTransaction = new UserTransactionImp();
        return new JtaTransactionManager(userTransaction, userTransactionManager);
    }
}

四、数据库文件配置

Springboot 2.X——jta-atomikos+mybatis多数据源配置和事务管理_第2张图片

CREATE TABLE `users`  (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `descript` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `createtime` date DEFAULT NULL,
  PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

注意:要想 java 事务能够回滚,必须保证数据库本身支持事务,即数据库表类型为 ENGINE = InnoDB 

五、测试

本身还有很多代码,像业务层和控制层等,此处的重点不在全部的代码编写上,所以未添加完整代码,需要完整代码的在下面的github中可以clone和下载。

自己完善了单数据源的CRUD操作,事务正常,操作两个数据源的事务,设定异常也都能进行回滚操作。

六、完整代码下载

https://github.com/765199214/springboot-jta-mybatis

参考资料:《atomikos JTA/XA全局事务》、田守枝《4.0 atomikos JTA/XA全局事务》

 

你可能感兴趣的:(springboot)