springboot+JTA+atomikos多数据源分布式事务管理

一、项目需求

        1、同时操作两个数据库,一个在本地服务器,一个在云服务器。

        2、数据库数据是同步的,两个数据库同时做update、insert等操作时,无论哪个数据库操作失败,要求两个数据库数据同时回滚。

        3、两个数据库均为SQL server数据库。

二、特殊要求

        1、数据库支持XA事务。网上其他教程都很复杂,但是这个博主写的很不错,不会配的可以参考一下:无法创建 XA 控制连接。错误:“找不到存储过程 'master..xp_sqljdbc_xa_init_ex'_com.microsoft.sqlserver.jdbc.sqlserverexception: 无-CSDN博客

        2、设置允许远程操作数据库。

        3、云服务器在控制台配置入站或出站规则,tcp协议,开放数据库端口。本地数据库在防火墙配置入站出站规则。

三、简单认识XA、JTA和Atomikos

XA

        XA是定义于数据库的分布式事务处理规范,XA事务支持不同数据库之间实现分布式事务。

JTA

        JTA(Java Transaction API):是Java平台上一个标准API,用于管理和控制分布式事务的执行流程。它是数据库XA事务在Java的一个映射。

        核心类:
javax.transaction.UserTransaction:暴露给应用使用,用来启动、提交、回滚事务。
javax.transaction.TransactionManager:提供给事务管理器的接口,用于协调和控制分布式事务的执行过程。
javax.transaction.XAResource:表示一个资源管理器,用于管理和操作资源。
javax.transaction.Xid:用于唯一标识一个分布式事务。

Atomikos

        Atomikos是一个开源的事务管理器,用于管理和控制分布式事务的执行过程。Atomikos可以解决,在同一个应用下,连接多个数据库,实现分布式事务。

四、项目案例

项目目录:

0、pom配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.13</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<groupId>com.gloyel</groupId>
	<artifactId>twodbgn</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>twodbgn</name>
	<description>twodbgn</description>

	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.32</version>
		</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-test</artifactId>
			<version>3.0.3</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jta-atomikos</artifactId>
			<version>2.6.13</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
			<version>3.6.0</version>
		</dependency>
		<dependency>
			<groupId>jakarta.transaction</groupId>
			<artifactId>jakarta.transaction-api</artifactId>
			<version>1.3.3</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>5.3.23</version>
		</dependency>
		<dependency>
			<groupId>com.microsoft.sqlserver</groupId>
			<artifactId>mssql-jdbc</artifactId>
			<version>11.2.0.jre17</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

1、bean

        User是实体类,与数据库表对应可自行配置

2、config

        JTA 事务配置,固定配置,通过JTATransactionManager实现分布式事务

import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;

/**
 * JTA 事务配置
 */
@Configuration
public class AtomikosConfig {
    @Bean(name = "userTransaction")
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(10000);
        return userTransactionImp;
    }

    @Bean(name = "atomikosTransactionManager")
    public TransactionManager atomikosTransactionManager() throws Throwable {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(false);
        return userTransactionManager;
    }

    @Bean(name = "transactionManager")
    @DependsOn({ "userTransaction", "atomikosTransactionManager" })
    public PlatformTransactionManager transactionManager() throws Throwable {
        UserTransaction userTransaction = userTransaction();
        TransactionManager atomikosTransactionManager = atomikosTransactionManager();
        return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
    }
}

3、controller

        控制层

import com.gloyel.twodbgn.Service.UserServiceImpl;
import com.gloyel.twodbgn.Service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService=new UserServiceImpl();

    @RequestMapping(value = "/userList")
    public String addPre() {
        String msg = "";
        try {
            userService.updateUser("and FID = 7");
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        return msg;
    }
}

4、ioc

        配置数据库

import java.sql.SQLException;

import javax.sql.DataSource;

import com.atomikos.icatch.jta.UserTransactionManager;
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.*;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.microsoft.sqlserver.jdbc.SQLServerXADataSource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;


@Configuration
@MapperScan(basePackages="com.gloyel.twodbgn.mapper.local",sqlSessionTemplateRef="localSqlSessionTemplate")
public class TestMybatisConfig1 {

    @Primary
    @Bean(name="localDataSource")
    @DependsOn("transactionManager")
    public DataSource testDataSource() throws SQLException {
        SQLServerXADataSource sqlServerXADataSource = new SQLServerXADataSource();
        sqlServerXADataSource.setURL("jdbc:sqlserver://localhost:1433;databaseName=mytest;encrypt=true;trustServerCertificate=true");
        sqlServerXADataSource.setUser("username");
        sqlServerXADataSource.setPassword("password");

        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(sqlServerXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("localDataSource");

        atomikosDataSourceBean.setMinPoolSize(3);
        atomikosDataSourceBean.setMaxPoolSize(25);

        return atomikosDataSourceBean;
    }
    @Primary
    @Bean(name="localSqlSessionFactory")
    @DependsOn("localDataSource")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("localDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }
    @Primary
    @Bean(name="localSqlSessionTemplate")
    @DependsOn("localSqlSessionFactory")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("localSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

/***************************************************************************************/

import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.microsoft.sqlserver.jdbc.SQLServerXADataSource;
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 org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import java.sql.SQLException;

@Configuration
@MapperScan(basePackages="com.gloyel.twodbgn.mapper.cloud",sqlSessionTemplateRef="cloudSqlSessionTemplate")
public class TestMybatisConfig2 {

    @Bean(name="cloudDataSource")
    @DependsOn("transactionManager")
    public DataSource testDataSource() throws SQLException {
        SQLServerXADataSource sqlServerXADataSource = new SQLServerXADataSource();
        sqlServerXADataSource.setURL("jdbc:sqlserver://ip:端口;databaseName=mytest;encrypt=true;trustServerCertificate=true");
        sqlServerXADataSource.setUser("username");
        sqlServerXADataSource.setPassword("password");

        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(sqlServerXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("cloudDataSource");

        atomikosDataSourceBean.setMinPoolSize(3);
        atomikosDataSourceBean.setMaxPoolSize(25);

        return atomikosDataSourceBean;
    }
    @Bean(name="cloudSqlSessionFactory")
    @DependsOn("cloudDataSource")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("cloudDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }
    @Bean(name="cloudSqlSessionTemplate")
    @DependsOn("cloudSqlSessionFactory")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("cloudSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

5、mapper

@Mapper
public interface UserMapper_C {
    @Select({"select * from User_loginData where 1=1 ${where}"})
    List<User> selectUser(@Param("where")String where);

    @Update({"update Dc_User set FPassWord = 'ccc' where 1=1 ${where}"})
    int updateUser(@Param("where")String where);

}


@Mapper
public interface UserMapper_L {
    @Select({"select * from User_loginData where 1=1 ${where}"})
    List<User> selectUser(@Param("where")String where);

    @Update({"update Dc_User set FPassWord = 'lll' where 1=1 ${where}"})
    int updateUser(@Param("where")String where);

}

6、service

public interface UserService {
    List<User> selectUser(String where);
    int updateUser(String where);
}
@Service("userService")
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper_L userMapper_l;
    @Resource
    private UserMapper_C userMapper_c;

    @Override
    public List<User> selectUser(String where) {
        return userMapper_c.selectUser(where);
    }

    @Override
    @Transactional
    public int updateUser(String where) {
        int a = userMapper_c.updateUser(where);
//        int c = 1/0;
        int b = userMapper_l.updateUser(where);
        System.out.println("a="+a+" -- b="+b);
        return a+b;
    }
}

五、调试运行

1、运行正常时:

//UserMapper_C 
@Update({"update Dc_User set FPassWord = 'ccc' where 1=1 ${where}"})
int updateUser(@Param("where")String where);

//UserMapper_L
@Update({"update Dc_User set FPassWord = 'lll' where 1=1 ${where}"})
int updateUser(@Param("where")String where);


@Override
    @Transactional
    public int updateUser(String where) {
        int a = userMapper_c.updateUser(where);
//        int c = 1/0;
        int b = userMapper_l.updateUser(where);
        System.out.println("a="+a+" -- b="+b);
        return a+b;
    }

控制台:

云数据库:

本地数据库:

2、运行异常时:

        观察出现异常时,更新语句是否回滚。

//UserMapper_C 
@Update({"update Dc_User set FPassWord = 'ccc000' where 1=1 ${where}"})
int updateUser(@Param("where")String where);

//UserMapper_L
@Update({"update Dc_User set FPassWord = 'lll000' where 1=1 ${where}"})
int updateUser(@Param("where")String where);


@Override
    @Transactional
    public int updateUser(String where) {
        int a = userMapper_c.updateUser(where);
        int c = 1/0;
        int b = userMapper_l.updateUser(where);
        System.out.println("a="+a+" -- b="+b);
        return a+b;
    }

控制台:

云数据库:

本地数据库:

至此,多数据源分布式事务管理项目测试成功!

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 首先,为了使用多数据源分布式事务,我们需要添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.6</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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> </dependency> ``` 接下来,我们需要在application.properties文件中配置数据源和事务管理器: ```properties # 配置主数据源 spring.datasource.url=jdbc:mysql://localhost:3306/main_db?characterEncoding=utf8&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # 配置从数据源 spring.datasource.slave.url=jdbc:mysql://localhost:3306/slave_db?characterEncoding=utf8&useSSL=false spring.datasource.slave.username=root spring.datasource.slave.password=root spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver # 配置Mybatis mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.example.entity # 配置Druid数据源 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initial-size=1 spring.datasource.druid.max-active=10 spring.datasource.druid.min-idle=1 spring.datasource.druid.max-wait=60000 spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.min-evictable-idle-time-millis=300000 spring.datasource.druid.test-while-idle=true spring.datasource.druid.test-on-borrow=false spring.datasource.druid.test-on-return=false spring.datasource.druid.filters=stat,wall,log4j spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 配置事务管理spring.transaction.default-timeout=600 spring.transaction.rollback-on-commit-failure=true spring.transaction.allow-bean-definition-overriding=true spring.transaction.jta.registry-name=atomikos spring.jta.enabled=true spring.jta.atomikos.connectionfactory.min-pool-size=5 spring.jta.atomikos.connectionfactory.max-pool-size=10 spring.jta.atomikos.connectionfactory.borrow-connection-timeout=30 spring.jta.atomikos.connectionfactory.max-idle-time=60 spring.jta.atomikos.connectionfactory.concurrency-level=100 ``` 然后,我们需要创建两个数据源的配置类,分别为主数据源和从数据源: ```java @Configuration @MapperScan(basePackages = "com.example.mapper.main", sqlSessionTemplateRef = "mainSqlSessionTemplate") public class MainDataSourceConfig { @Bean(name = "mainDataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource mainDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "mainSqlSessionFactory") public SqlSessionFactory mainSqlSessionFactory(@Qualifier("mainDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/main/*.xml")); return bean.getObject(); } @Bean(name = "mainTransactionManager") public DataSourceTransactionManager mainTransactionManager(@Qualifier("mainDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "mainSqlSessionTemplate") public SqlSessionTemplate mainSqlSessionTemplate(@Qualifier("mainSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } } ``` ```java @Configuration @MapperScan(basePackages = "com.example.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate") public class SlaveDataSourceConfig { @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "slaveSqlSessionFactory") public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml")); return bean.getObject(); } @Bean(name = "slaveTransactionManager") public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "slaveSqlSessionTemplate") public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } } ``` 最后,我们需要在事务管理器上添加注解@EnableTransactionManagement,并在需要使用事务的方法上添加注解@Transactional: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Transactional(rollbackFor = Exception.class, transactionManager = "mainTransactionManager") @Override public void save(User user) { userMapper.insert(user); } @Transactional(rollbackFor = Exception.class, transactionManager = "slaveTransactionManager") @Override public User findById(int id) { return userMapper.selectByPrimaryKey(id); } } ``` 以上就是使用SpringBoot+Mybatis+druid多数据源分布式事务的基本步骤。 ### 回答2: Spring Boot是一个用于构建独立的、生产级的应用程序的框架。它简化了应用程序的开发过程,并通过自动配置来减少了繁琐的配置。MyBatis是一个ORM(对象关系映射)框架,它提供了将数据库操作映射到Java对象的功能。Druid是一种高性能的数据库连接池。 要在Spring Boot中使用MyBatis和Druid进行多数据源配置和分布式事务管理,可以按照以下步骤进行操作: 1. 添加依赖:在项目的pom.xml文件中,添加Spring Boot、MyBatis和Druid的依赖。 2. 配置数据源:在application.properties文件中,配置并命名多个数据源,设置数据库连接等信息。 3. 创建数据源配置类:创建一个配置类,使用@Configuration注解将其标记为配置类,并使用@ConfigurationProperties注解将数据源属性注入。 4. 创建数据源:根据配置类中的属性,创建多个数据源,并将其加入到数据源路由器中。 5. 配置MyBatis:创建一个配置类,使用@MapperScan注解设置MyBatis的mapper接口路径,并将数据源注入到SqlSessionFactory中。 6. 配置分布式事务:使用@EnableTransactionManagement注解启用事务管理,并配置事务管理器。 7. 编写数据库操作代码:在mapper接口中定义数据库操作方法,并在Service层中调用这些方法进行数据库操作。 通过以上步骤,你就可以在Spring Boot项目中完成MyBatis和Druid的多数据源配置和分布式事务管理。不过需要注意的是,使用多数据源分布式事务会增加项目的复杂性和性能开销,所以在使用之前需要仔细考虑是否真正需要这些功能。 ### 回答3: Spring Boot是一种快速构建Java应用程序的框架,MyBatis是一种流行的Java持久化框架,Druid是一种高性能的数据库连接池。本文将介绍如何在Spring Boot中使用MyBatis和Druid来实现多数据源分布式事务。 要使用多个数据源,我们首先需要配置多个数据源。在Spring Boot中,我们可以通过在application.properties或者application.yml文件中配置多个数据源的连接信息。我们需要为每个数据源指定不同的URL、用户名和密码。然后,我们可以使用@Primary和@Qualifier来指定主数据源和其他数据源。 在配置数据源后,我们需要配置MyBatis来使用这些数据源。我们可以通过创建多个SqlSessionFactory来实现多数据源,然后在每个SqlSessionFactory中设置相应的数据源。我们还可以使用@MapperScan注解来自动扫描和注册Mapper接口。 在使用MyBatis和多个数据源时,我们可能会遇到事务管理问题。为了解决这个问题,我们可以使用Spring Boot提供的@Transactional注解来标记需要进行事务管理的方法,然后Spring Boot会自动为我们处理事务。对于需要跨多个数据源进行事务管理的情况,我们可以使用JTA(Java Transaction API)实现分布式事务。在Spring Boot中,我们可以使用Atomikos或Bitronix等JTA提供商来实现分布式事务。 总结起来,使用Spring Boot、MyBatis和Druid,我们可以很容易地实现多数据源分布式事务。通过正确配置数据源和使用相关注解,我们可以在几分钟内完成这些任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值