在 pom.xml 中引入 jta-atomikos
以及测试包
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.1.1
org.springframework.boot
spring-boot-starter-jta-atomikos
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-test
test
org.springframework
spring-test
org.springframework
spring-test
5.0.5.RELEASE
test
在 application.properties
中添加两个数据源的配置
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/mysql
mysql.datasource.test1.username = root
mysql.datasource.test1.password = 123456
mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test2.url = jdbc:mysql://localhost:3306/mybatis
mysql.datasource.test2.username =root
mysql.datasource.test2.password = 123456
mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
使用 application.yml
的配置为
### two datasource Configuration
mysql:
datasource:
test1:
url: jdbc:mysql://localhost:3306/mysql
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
minPoolSize: 3
maxPoolSize: 25
maxLifetime: 20000
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60
test2:
url: jdbc:mysql://localhost:3306/mybatis
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
minPoolSize: 3
maxPoolSize: 25
maxLifetime: 20000
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60
添加两个 bean
文件 src/main/java/com/bean/MybatisUser.java
和 src/main/java/com/bean/MysqlUser.java
。
src/main/java/com/bean/MybatisUser.java
package com.bean;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @Description 类描述
* @author 欧阳
* @since 2019年4月7日 下午12:29:13
* @version V1.0
*/
@Getter
@Setter
@ToString
public class MybatisUser {
private String id;
private String name;
public MybatisUser() {
super();
}
public MybatisUser(String id, String name) {
super();
this.id = id;
this.name = name;
}
}
src/main/java/com/bean/MysqlUser.java
package com.bean;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @Description 类描述
* @author 欧阳
* @since 2019年4月7日 下午12:29:13
* @version V1.0
*/
@Getter
@Setter
@ToString
public class MysqlUser {
private String id;
private String name;
public MysqlUser() {
super();
}
public MysqlUser(String id, String name) {
super();
this.id = id;
this.name = name;
}
}
根据库的区别,创建两个包 com.mysql
和 com.mybatis
,分别在包下创建 Mapper 。
在 com.mysql
包下创建 src/main/java/com/mysql/UserMapper2.java
和 src/main/java/com/mysql/userMapper2.xml
insert into t_user(id, name) values(#{id}, #{name})
update t_user set name=#{name} where id=#{id}
delete from t_user where id = #{id}
package com.mysql;
import java.util.List;
import com.bean.MysqlUser;
/**
* @Description springboot 整合jta-atomikos 数据源2
* @author 欧阳
* @since 2019年4月11日 下午4:26:00
* @version V1.0
*/
public interface UserMapper2 {
/**
* 添加用户
* @param user
*/
public void insertUser(MysqlUser mysqlUser);
/**
* 更新用户
* @param user
*/
public void updateUser(MysqlUser mysqlUser);
/**
* 查询用户
* @param user
* @return
*/
public List selectUsersAll();
/**
* 删除用户
* @param id
*/
public void deleteUserById(String id);
}
在 com.mysql
包下创建 src/main/java/com/mybatis/UserMapper1.java
和 src/main/java/com/mybatis/userMapper1.xml
insert into t_user(id, name) values(#{id}, #{name})
update t_user set name=#{name} where id=#{id}
delete from t_user where id = #{id}
package com.mybatis;
import java.util.List;
import com.bean.MybatisUser;
/**
* @Description springboot 整合jta-atomikos 数据源1
* @author 欧阳
* @since 2019年4月11日 下午4:26:00
* @version V1.0
*/
public interface UserMapper1 {
/**
* 添加用户
* @param user
*/
public void insertUser(MybatisUser mybatisUser);
/**
* 更新用户
* @param user
*/
public void updateUser(MybatisUser mybatisUser);
/**
* 查询用户
* @param user
* @return
*/
public List selectUsersAll();
/**
* 删除用户
* @param id
*/
public void deleteUserById(String id);
}
package com.config;
import java.sql.SQLException;
import javax.sql.DataSource;
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.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import lombok.Data;
/**
* @Description 配置多数据源
* @author 欧阳
* @since 2019年6月15日 下午5:16:47
* @version V1.0
*/
@Configuration
public class MultiDBConfig {
@Bean
@ConfigurationProperties(prefix = "mysql.datasource.test1")
public DBConfig1 dbConfig1() {
return new DBConfig1();
}
@Bean
@ConfigurationProperties(prefix = "mysql.datasource.test2")
public DBConfig2 dbConfig2() {
return new DBConfig2();
}
@Component
@MapperScan(basePackages = {"com.mysql", "com.bean"}, sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class MyBatisConfig1 {
// 配置数据源
@Primary
@Bean(name = "db1DataSource")
public DataSource db1DataSource(DBConfig1 dbConfig1) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dbConfig1.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(dbConfig1.getPassword());
mysqlXaDataSource.setUser(dbConfig1.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean db1DataSource = new AtomikosDataSourceBean();
db1DataSource.setXaDataSource(mysqlXaDataSource);
db1DataSource.setUniqueResourceName("db1DataSource");
db1DataSource.setMinPoolSize(dbConfig1.getMinPoolSize());
db1DataSource.setMaxPoolSize(dbConfig1.getMaxPoolSize());
db1DataSource.setMaxLifetime(dbConfig1.getMaxLifetime());
db1DataSource.setBorrowConnectionTimeout(dbConfig1.getBorrowConnectionTimeout());
db1DataSource.setLoginTimeout(dbConfig1.getLoginTimeout());
db1DataSource.setMaintenanceInterval(dbConfig1.getMaintenanceInterval());
db1DataSource.setMaxIdleTime(dbConfig1.getMaxIdleTime());
db1DataSource.setTestQuery(dbConfig1.getTestQuery());
return db1DataSource;
}
@Primary
@Bean(name = "db1SqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource db1DataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(db1DataSource);
return bean.getObject();
}
@Primary
@Bean(name = "db1SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory db1SqlSessionFactory) throws Exception {
return new SqlSessionTemplate(db1SqlSessionFactory);
}
}
@Component
@MapperScan(basePackages = {"com.mybatis", "com.bean"}, sqlSessionTemplateRef = "db2SqlSessionTemplate")
public class MyBatisConfig2 {
// 配置数据源
@Bean(name = "db2DataSource")
public DataSource db2DataSource(DBConfig2 dbConfig2) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dbConfig2.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(dbConfig2.getPassword());
mysqlXaDataSource.setUser(dbConfig2.getUsername());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean db2DataSource = new AtomikosDataSourceBean();
db2DataSource.setXaDataSource(mysqlXaDataSource);
db2DataSource.setUniqueResourceName("db2DataSource");
db2DataSource.setMinPoolSize(dbConfig2.getMinPoolSize());
db2DataSource.setMaxPoolSize(dbConfig2.getMaxPoolSize());
db2DataSource.setMaxLifetime(dbConfig2.getMaxLifetime());
db2DataSource.setBorrowConnectionTimeout(dbConfig2.getBorrowConnectionTimeout());
db2DataSource.setLoginTimeout(dbConfig2.getLoginTimeout());
db2DataSource.setMaintenanceInterval(dbConfig2.getMaintenanceInterval());
db2DataSource.setMaxIdleTime(dbConfig2.getMaxIdleTime());
db2DataSource.setTestQuery(dbConfig2.getTestQuery());
return db2DataSource;
}
@Bean(name = "db2SqlSessionFactory")
public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource db2DataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(db2DataSource);
return bean.getObject();
}
@Bean(name = "db2SqlSessionTemplate")
public SqlSessionTemplate db2SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory db2SqlSessionFactory) throws Exception {
return new SqlSessionTemplate(db2SqlSessionFactory);
}
}
@Data
private 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;
}
@Data
private 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;
}
}
添加一个统一的 Service 接口 src/main/java/com/service/MoreDateSourceService.java
并实现该接口,实现类 src/main/java/com/service/impl/MoreDateSourceServiceImpl.java
统一调用两个数据源,测试整合 jta-atomikos
实现分布式事务管理的正确性,以向两个数据库中添加用户为例。
MoreDateSourceService 接口代码
package com.service;
import com.bean.MybatisUser;
import com.bean.MysqlUser;
/**
* @Description 多数据源事务管理 Service
* @author 欧阳
* @since 2019年4月11日 下午8:34:50
* @version V1.0
*/
public interface MoreDateSourceService {
public void addUser(MybatisUser mybatisUser, MysqlUser mysqlUser);
}
MoreDateSourceServiceImpl 实现类
package com.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.bean.MybatisUser;
import com.bean.MysqlUser;
import com.mybatis.UserMapper1;
import com.mysql.UserMapper2;
import com.service.MoreDateSourceService;
/**
* @Description 多数据源事务管理 Service
* @author 欧阳
* @since 2019年4月11日 下午8:35:42
* @version V1.0
*/
@Service
public class MoreDateSourceServiceImpl implements MoreDateSourceService {
@Autowired
private UserMapper2 userMapper2;
@Autowired
private UserMapper1 userMapper1;
@Override
@Transactional
public void addUser(MybatisUser mybatisUser, MysqlUser mysqlUser) {
userMapper1.insertUser(mybatisUser);
userMapper2.insertUser(mysqlUser);
// int num = 1/0; //放开测试事务。预期结果为两个库都添加失败,库中无数据。测试结果与预期一致
}
}
注意:通过使用 @Transactional
注解添加事务管理。第一种情况,在 addUser
方法中的插入数据代码后增加异常代码 int num = 1/0;
,测试事务是否起作用,如果不起作用,则两个库中都有刚插入的数据,反之两个库中都没有刚插入的数据;第二种情况,注释异常代码 int num = 1/0;
,数据会正常插入到两个库中。测试后实际测试结果表明测试结果与预期一致,整合 jta-atomikos
后可通过使用 @Transactional
注解可管理分布式事务。
添加测试类测试 MoreDateSourceServiceImplTest
package com.service.impl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.App;
import com.bean.MybatisUser;
import com.bean.MysqlUser;
import com.service.MoreDateSourceService;
/**
* @Description 多数据源事务管理 测试
* @author 欧阳
* @since 2019年4月11日 下午8:45:21
* @version V1.0
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes={App.class})
public class MoreDateSourceServiceImplTest {
@Autowired
private MoreDateSourceService moreDateSourceService;
@Test
public void testAddUser() {
MybatisUser mybatisUser = new MybatisUser("12", "张三");
MysqlUser mysqlUser = new MysqlUser("12", "张三");
moreDateSourceService.addUser(mybatisUser, mysqlUser);
System.out.println("Yes");
}
}
测试结果通过,通过整合 jta-atomikos
实现分布式事务管理功能正常,springboot 整合 jta-atomikos
完成。