使用 springboot
+ jta
+ atomikos
分布式事物管理
Atomikos 是一个为 Java 平台提供增值服务的并且开源类事务管理器。
1 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- springboot-web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
新增 jta-atomikos
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
2 新增配置文件信息
mysql:
datasource:
test1: # Mysql 1
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60
maxLifetime: 20000
maxPoolSize: 25
minPoolSize: 3
password: 123456
url: jdbc:mysql://120.78.134.111:3306/test01?useUnicode=true&characterEncoding=utf-8
username: root
test2: # Mysql 2
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60
maxLifetime: 20000
maxPoolSize: 25
minPoolSize: 3
password: 123456
url: jdbc:mysql://120.78.134.111:3306/test02?useUnicode=true&characterEncoding=utf-8
username: root
3 读取配置文件信息
package com.snow.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
@Data
@ConfigurationProperties(prefix = "mysql.datasource.test1")
public class DBConfig1 {
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.snow.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
@Data
@ConfigurationProperties(prefix = "mysql.datasource.test2")
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;
}
4 创建多数据源
package com.snow.datasource;
import java.sql.SQLException;
import javax.sql.DataSource;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.snow.config.DBConfig1;
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.snow.test01", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class MyBatisConfig1 {
// 配置数据源
@Bean(name = "testDataSource")
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("testDataSource");
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 = "testSqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "testSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
package com.snow.datasource;
import java.sql.SQLException;
import javax.sql.DataSource;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.snow.config.DBConfig2;
@Configuration
@MapperScan(basePackages = "com.snow.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {
// 配置数据源
@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);
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;
}
@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
5 启动项
package com.snow;
import com.snow.config.DBConfig1;
import com.snow.config.DBConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
// 开启读取配置文件
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })
@SpringBootApplication
public class SnowApp {
public static void main(String[] args) {
SpringApplication.run(SnowApp.class);
}
}
6 实体类
package com.snow.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private int age;
private String name;
}
7 创建分包 test01
package com.snow.test01.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.snow.pojo.User;
public interface UserMapperTest01 {
// 查询语句
@Select("SELECT * FROM user WHERE name = #{name}")
User findByName(@Param("name") String name);
// 添加
@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
}
package com.snow.test01.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.snow.test01.mapper.UserMapperTest01;
import lombok.extern.slf4j.Slf4j;
/**
* UserServiceTest01<br>
*
*/
@Service
@Slf4j
public class UserServiceTest01 {
@Autowired
private UserMapperTest01 userMapperTest01;
@Transactional(transactionManager = "test1TransactionManager")
public int insertUser(String name, Integer age) {
int insertUserResult = userMapperTest01.insert(name, age);
log.info("######insertUserResult:{}##########", insertUserResult);
int i = 1 / age;
// 怎么样验证事务开启成功!~
return insertUserResult;
}
}
8 创建分包 test02
package com.snow.test02.mapper;
import com.snow.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapperTest02 {
// 查询语句
@Select("SELECT * FROM user WHERE name = #{name}")
User findByName(@Param("name") String name);
// 添加
@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
}
package com.snow.test02.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.snow.test01.mapper.UserMapperTest01;
import com.snow.test02.mapper.UserMapperTest02;
import lombok.extern.slf4j.Slf4j;
/**
* UserServiceTest02<br>
*
*/
@Service
@Slf4j
public class UserServiceTest02 {
@Autowired
private UserMapperTest02 userMapperTest02;
@Autowired
private UserMapperTest01 userMapperTest01;
@Transactional(transactionManager = "test2TransactionManager")
public int insertUser(String name, Integer age) {
int insertUserResult = userMapperTest02.insert(name, age);
log.info("######insertUserResult:{}##########", insertUserResult);
// 怎么样验证事务开启成功!~
int i = 1 / age;
return insertUserResult;
}
@Transactional()
public int insertUserTest01AndTest02(String name, Integer age) {
// 传统分布式事务解决方案 jta+atomikos 注册同一个全局事务中
// 第一个数据源
int insertUserResult01 = userMapperTest01.insert(name, age);
// 第二个数据源
int insertUserResult02 = userMapperTest02.insert(name, age);
int i = 1 / 0;
int result = insertUserResult01 + insertUserResult02;
// test01入库 test02回滚
return result;
}
}
9 控制层
package com.snow.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.snow.test01.service.UserServiceTest01;
import com.snow.test02.service.UserServiceTest02;
/**
* 多数据源测试<br>
*
*/
@RestController
public class MybatisMultilDataSourceController {
@Autowired
private UserServiceTest01 userServiceTest01;
@Autowired
private UserServiceTest02 userServiceTest02;
@RequestMapping("/insertUserTest1")
public Integer insertUserTest1(String name, Integer age) {
return userServiceTest01.insertUser(name, age);
}
@RequestMapping("/insertUserTest2")
public Integer insertUserTest2(String name, Integer age) {
return userServiceTest02.insertUser(name, age);
}
@RequestMapping("/insertUserTest01AndTest02")
public int insertUserTest01AndTest02(String name, Integer age) {
return userServiceTest02.insertUserTest01AndTest02(name, age);
}
}