Springboot 整合jta-atomikos实现多数据源及分布式事务管理——SpringBoot学习

一、POM 文件引入 JAR

  在 pom.xml 中引入 jta-atomikos 以及测试包

<!-- mybatis 依赖 -->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>1.1.1</version>
</dependency>

<!-- atomikos 多数据源管理 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

<!-- mysql 依赖 -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 测试 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
	<exclusions>
		<exclusion>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<!-- SpringBoot 2.0 的 spring-boot-starter-test 中的spring-test 是5.0.4 版本的,
		而,5.0.4 版没有 SpringJUnit4ClassRunner 和 SpringRunner 所以去除,另加入5.0.5 及以上版本的spring-test-->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>5.0.5.RELEASE</version>
	<scope>test</scope>
</dependency>

二、application.properties 新增配置

  在 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
  

三、编写 Dao

  添加两个 bean 文件 src/main/java/com/bean/MybatisUser.javasrc/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.mysqlcom.mybatis ,分别在包下创建 Mapper 。

  在 com.mysql 包下创建 src/main/java/com/mysql/UserMapper2.javasrc/main/java/com/mysql/userMapper2.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mysql.UserMapper2">

	<insert id="insertUser" parameterType="com.bean.MysqlUser">
		insert into t_user(id, name) values(#{id}, #{name})
	</insert>

	<select id="selectUsersAll" resultType="com.bean.MysqlUser">
		select id,name from t_user
	</select>

	<update id="updateUser" parameterType="com.bean.MysqlUser">
		update t_user set name=#{name} where id=#{id}
	</update>

	<delete id="deleteUserById" parameterType="string">
		delete from t_user where id = #{id}
	</delete>
</mapper>
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<MysqlUser> selectUsersAll();
	
	/**
	 * 删除用户
	 * @param id
	 */
	public void deleteUserById(String id);
}

  在 com.mysql 包下创建 src/main/java/com/mybatis/UserMapper1.javasrc/main/java/com/mybatis/userMapper1.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.UserMapper1">

	<insert id="insertUser" parameterType="com.bean.MybatisUser">
		insert into t_user(id, name) values(#{id}, #{name})
	</insert>

	<select id="selectUsersAll" resultType="com.bean.MybatisUser">
		select id,name from t_user
	</select>

	<update id="updateUser" parameterType="com.bean.MybatisUser">
		update t_user set name=#{name} where id=#{id}
	</update>

	<delete id="deleteUserById" parameterType="string">
		delete from t_user where id = #{id}
	</delete>
</mapper>
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<MybatisUser> 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

  添加一个统一的 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 完成。

测试结果成功

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lytao123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值