您现在的位置:首页 > >

spring boot + mybatis plus + 多数据源 + 分布式事务

发布时间:

本文介绍一种使用?spring boot + mybatis plus + 多数据源 + 分布式事务 的实现方式。


由于涉及多个数据库,自然就涉及到分布式事务。先了解几个概念:


1、XA是什么?


XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。


2、Java事务API(Java Transaction API,简称JTA ) 是一个Java企业版 的应用程序接口,在Java环境中,允许完成跨越多个XA资源的分布式事务。


3、Atomikos 是一个为Java*台提供增值服务的并且开源类事务管理器。


通过导入



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


即可实现分布式事务


Talk is cheap, I will show you the code。开始上代码


pom.xml



xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

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


com.example
multi-datasource
0.0.1-SNAPSHOT
multi-datasource
Demo project for Spring Boot


1.8




org.springframework.boot
spring-boot-starter-web



org.projectlombok
lombok
true




com.baomidou
mybatis-plus-boot-starter
3.3.0


mysql
mysql-connector-java
5.1.47



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



com.alibaba
druid
1.1.12



com.baomidou
mybatis-plus-generator
3.3.0


org.apache.velocity
velocity-engine-core
2.2



org.springframework.boot
spring-boot-starter-test
test


org.junit.vintage
junit-vintage-engine






multi-datasource



src/main/java

**/*.xml

false



src/main/resources

**/*.*





org.springframework.boot
spring-boot-maven-plugin





application.yml


spring:
jta:
# 事务管理器唯一标识符
transaction-manager-id: txManager
datasource:
# Druid连接池配置。spring-boot-2默认连接池hikari不支持MysqlXADataSource
type: com.alibaba.druid.pool.xa.DruidXADataSource
# 最小空闲连接
min-pool-size: 5
# 池中最大连接数
max-pool-size: 20
# 设置连接在池中被自动销毁之前保留的最大秒数。 可选,默认为0(无限制)。
max-life-time: 60
# 返回连接前用于测试连接的SQL查询
test-query: SELECT 1

# 多数据源配置
cpq-db:
name: cpq
url: jdbc:mysql://localhost:3306/cpq?useUnicode=true&characterEncoding=utf8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: cpq..123
shiro-db:
name: shiro
url: jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: cpq..123

mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql

配置数据源的DataSource、SqlSessionFactory


package com.example.multidatasource.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;

/**
* cpq数据库配置类
*/
@Configuration
@MapperScan(basePackages = "com.example.multidatasource.cpq.**.mapper",
sqlSessionFactoryRef = CpqDataSourcesConfig.SQL_SESSION_FACTORY)
public class CpqDataSourcesConfig {

public static final String DATABASE_PREFIX = "spring.datasource.cpq-db.";

public static final String DATA_SOURCE_NAME = "cpqDataSource";
public static final String SQL_SESSION_FACTORY = "cpqSqlSessionFactory";


/**
* 通过配置文件创建DataSource,一个数据库对应一个DataSource
* @param environment 环境变量,spring-boot会自动将IOC中的environment实例设置给本参数值
* 由于IOC中有多个DataSource实例,必须给其中一个实例加上@Primary
*/
@Primary
@Bean(DATA_SOURCE_NAME)
public DataSource dataSource(Environment environment) {
return DataSourceUtil.createAtomikosDataSourceBean(DATA_SOURCE_NAME, environment, DATABASE_PREFIX);
}

/**
* 通过dataSource创建SqlSessionFactory
* 由于IOC中有多个DataSource实例,必须给其中一个实例加上@Primary
*/
@Primary
@Bean(name = SQL_SESSION_FACTORY)
public SqlSessionFactory sqlSessionFactory(@Qualifier(DATA_SOURCE_NAME) DataSource dataSource) throws Exception {
return DataSourceUtil.createSqlSessionFactory(dataSource);
}

}

package com.example.multidatasource.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;

/**
* shiro数据库配置类
*/
@Configuration
@MapperScan(basePackages = "com.example.multidatasource.shiro.**.mapper",
sqlSessionFactoryRef = ShiroDataSourcesConfig.SQL_SESSION_FACTORY)
public class ShiroDataSourcesConfig {

public static final String DATABASE_PREFIX = "spring.datasource.shiro-db.";
public static final String DATA_SOURCE_NAME = "shiroDataSource";
public static final String SQL_SESSION_FACTORY = "shiroSqlSessionFactory";

@Bean(DATA_SOURCE_NAME)
public DataSource dataSource(Environment environment) {
return DataSourceUtil.createAtomikosDataSourceBean(DATA_SOURCE_NAME, environment, DATABASE_PREFIX);
}

@Bean(name = SQL_SESSION_FACTORY)
public SqlSessionFactory sqlSessionFactory(@Qualifier(DATA_SOURCE_NAME) DataSource dataSource) throws Exception {
return DataSourceUtil.createSqlSessionFactory(dataSource);
}
}

package com.example.multidatasource.config;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;

public class DataSourceUtil {

public static final String DATA_SOURCE_PREFIX = "spring.datasource.";

/**
* 创建AtomikosDataSourceBean是使用Atomikos连接池的首选类
*/
public static AtomikosDataSourceBean createAtomikosDataSourceBean(String uniqueResourceName, Environment environment, String dataBase ){
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
// 这些设置大家可以进入源码中看java-doc
// 数据源唯一标识
atomikosDataSourceBean.setUniqueResourceName(uniqueResourceName);
// XADataSource实现类,使用DruidXADataSource
atomikosDataSourceBean.setXaDataSourceClassName(environment.getProperty(DATA_SOURCE_PREFIX+"type"));
// 最小连接数,默认1
atomikosDataSourceBean.setMinPoolSize(environment.getProperty(DATA_SOURCE_PREFIX+"min-pool-size", Integer.class));
// 最大连接数,默认1
atomikosDataSourceBean.setMaxPoolSize(environment.getProperty(DATA_SOURCE_PREFIX+"max-pool-size", Integer.class));
// 设置连接在池中被自动销毁之前保留的最大秒数。 可选,默认为0(无限制)。
atomikosDataSourceBean.setMaxLifetime(environment.getProperty(DATA_SOURCE_PREFIX+"max-life-time", Integer.class));
// 返回连接前用于测试连接的SQL查询
atomikosDataSourceBean.setTestQuery(environment.getProperty(DATA_SOURCE_PREFIX+"test-query"));

MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setDatabaseName(environment.getProperty(dataBase+"name"));
mysqlXADataSource.setURL(environment.getProperty(dataBase+"url"));
mysqlXADataSource.setUser(environment.getProperty(dataBase+"username"));
mysqlXADataSource.setPassword(environment.getProperty(dataBase+"password"));
atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);

return atomikosDataSourceBean;
}

/**
* 创建SqlSessionFactory实例
*/
public static SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception{
/**
* 必须使用MybatisSqlSessionFactoryBean,
* 不能使用SqlSessionFactoryBean,不然会报invalid bound statement (not found)
*
* com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory(javax.sql.DataSource)
* 源码中也是使用MybatisSqlSessionFactoryBean
* 并且源码中使用了@ConditionalOnMissingBean,即IOC中如果存在了SqlSessionFactory实例,mybatis-plus就不创建SqlSessionFactory实例了
*/
MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
MybatisConfiguration configuration = new MybatisConfiguration();
sessionFactoryBean.setConfiguration(configuration);
return sessionFactoryBean.getObject();
}

}

新建一个service测试分布式事务


package com.example.multidatasource;

import com.example.multidatasource.cpq.girl.entity.Girl;
import com.example.multidatasource.cpq.girl.service.GirlService;
import com.example.multidatasource.shiro.role.entity.Role;
import com.example.multidatasource.shiro.role.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CommonService {

@Autowired
GirlService girlService;
@Autowired
RoleService roleService;

@Transactional(rollbackFor = Exception.class)
public boolean save(String msg){
Girl girl = new Girl();
girl.setName("name "+msg);
boolean b1 = girlService.save(girl);
Role role = new Role();
role.setRoleName("role-name"+msg);
boolean b2 = roleService.save(role);
//
//if (b2){
// throw new RuntimeException("RuntimeException");
//}

return b1 && b2;
}

}

工程完整代码地址在此:https://github.com/CodingSoldier/java-learn/tree/master/project/mysql-learn/multi-datasource


?


最后来验证过下连接池的配置是否生效。通过? SHOW PROCESSLIST;? ?可以查看数据库的连接数量,运行结果证明连接池的设置生效的。



?


?


?


友情链接: 医学资料大全 农林牧渔 幼儿教育心得 小学教育 中学 高中 职业教育 成人教育 大学资料 求职职场 职场文档 总结汇报