SpringBoot+Mybatis+atomikos实现分布式事务(分包扫描)

要实现SpringBoot+Mybatis+atomikos实现分布式事务(分包扫描),相对动态切换数据库来说简单很多。

1、引入依赖

2、配置文件

3、生成不同的数据库实例(bean)和相关工厂

1、引入依赖

 <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <scope>provided</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>fastjson</artifactId>
		    <version>1.2.62</version>
		</dependency>
		<dependency>
		    <groupId>org.mybatis.spring.boot</groupId>
		    <artifactId>mybatis-spring-boot-starter</artifactId>
		    <version>2.0.0</version>
		</dependency>
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>6.0.6</version>
		</dependency>
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>druid</artifactId>
		    <version>1.1.19</version>
		</dependency>
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <scope>provided</scope>
		</dependency>
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
		    <version>2.2.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

2、配置文件

server:
  port: 8025
   
spring:
  application:
        name: datasource_test
  datasource:
     master:
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://127.0.0.1:3306/citex_fusion?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE&serverTimezone=UTC&useSSL=true
     slave1:
         username: root
         password: root
         driver-class-name: com.mysql.cj.jdbc.Driver
         jdbc-url: jdbc:mysql://127.0.0.1:3306/citex_guess?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE&serverTimezone=UTC&useSSL=true
     druid:
        initial-size: 5
        max-active: 20
        min-idle: 5
        test-on-borrow: true
        max-wait: -1
        min-evictable-idle-time-millis: 30000
        max-evictable-idle-time-millis: 30000
        time-between-eviction-runs-millis: 0   
mybatis:
  configuration:
    map-underscore-to-camel-case: true

 3、生成不同的数据库实例(bean)和相关工厂

扫描不同包,生成不同的数据库相关(bean) 

工具类

public class PropertiesUtils {
	/**
	 * 读取数据库配置
	 * 
	 * @param env
	 * @param prefix
	 * @return
	 */
	public static Properties build(Environment env, String prefix) {
		String druidStr = "spring.datasource.druid.";
		Properties prop = new Properties();
		prop.put("username", env.getProperty(prefix + "username"));
		prop.put("password", env.getProperty(prefix + "password"));
		prop.put("url", env.getProperty(prefix + "jdbc-url"));
		prop.put("driverClassName", env.getProperty(prefix + "driver-class-name", ""));
		prop.put("initialSize", env.getProperty(druidStr + "initial-size", Integer.class));
		prop.put("maxActive", env.getProperty(druidStr + "max-active", Integer.class));
		prop.put("minIdle", env.getProperty(druidStr + "min-idle", Integer.class));
		prop.put("maxWait", env.getProperty(druidStr + "max-wait", Integer.class));
		prop.put("testOnBorrow", env.getProperty(druidStr + "test-on-borrow", Boolean.class));
		return prop;
	}

}

主数据库的配置

@Configuration
@MapperScan(basePackages = "com.hongyu.mapper.fusion", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {
	@Primary
	@Bean(name = "master")
	public DataSource masterDataSource(Environment env) {
		AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
		Properties prop = PropertiesUtils.build(env, "spring.datasource.master.");
		ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
		ds.setUniqueResourceName("master");
		ds.setXaProperties(prop);
		return ds;
	}

	@Primary
	@Bean(name = "masterSqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("master") DataSource dataSource) throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Primary
	@Bean(name = "masterSqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}

从数据库配置

@Configuration
@MapperScan(basePackages = "com.hongyu.mapper.guess", sqlSessionFactoryRef = "slaveOneSqlSessionFactory")
public class SlaveOneDataSourceConfig {
	@Primary
	@Bean(name = "slaveOne")
	public DataSource masterDataSource(Environment env) {
		AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
		Properties prop = PropertiesUtils.build(env, "spring.datasource.slave1.");
		ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
		ds.setUniqueResourceName("slaveOne");
		ds.setXaProperties(prop);
		return ds;
	}

	@Primary
	@Bean(name = "slaveOneSqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("slaveOne") DataSource dataSource) throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Primary
	@Bean(name = "slaveOneSqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("slaveOneSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

下面我们编写测试的例子

public interface FusionMapper {

	@Select("select  * from  fusion_invitee limit 10 ")
	List<FusionInvite> getFusionInviteList();


	@Insert("INSERT INTO fusion_invitee(user_id, invitee_id, code, create_time, update_time)\n"
			+ "VALUES (50018, 20597, 'FV3NEA', NOW(), NOW())")
	int insertfusionInvite(FusionInvite fusionInvite);

}

 

public interface GuessMapper {

	@Select("select  * from  guess_account limit 10")
	List<GuessAccount> getGuessAccountList();


	@Insert("INSERT INTO guess_account(USER_ID, CURRENCY_ID, CURRENCY, AVAILABLE_QTY, FROZEN_QTY, CREATE_TIME, UPDATE_TIME) VALUES "
			+ "(27491, 3, 'USDT', 0.007263000000000000, 0.000000000000000000, NOW(), NOW())")
	int insertguessAccount(GuessAccount guessAccount);
}
	@Transactional
	public void testJtaTransactional() {
		GuessAccount guessAccount = new GuessAccount();
		int row = guessMapper.insertguessAccount(guessAccount);
		FusionInvite fusionInvite = new FusionInvite();
		int row2 = fusionMapper.insertfusionInvite(fusionInvite);
		int i = 1 / 0;
	}

特别说明,如果没有进行第7步的改造的话,会出现无法切换数据源的情况,是由于我们使用了    @Transactional注解。

为了保证事物的一致性,它需要保证同一个线程的数据库执行Connection和事物执行的Connection必须保持一致,因此去调用下一个Mapper时仍然保持了上一个Mapper的连接。所以就报错了。

需要解决这个问题,就需要实现事物中的Connection动态切换。这样我们两段提交协议才能生效。

在启动了上面必须要排除SpringBoot自带数据库配置加载


@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableTransactionManagement
@MapperScan(basePackages = { "com.hongyu.mapper" })
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值