maven依赖

点击查看代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencies>
	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</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>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

.yml文件及相关bean配置

.yml配置

点击查看代码
spring:
    db1:
      url: jdbc:mysql://localhost:3306/xiaotianxian?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
      username: ***
      password: ***
      driver-class-name: com.mysql.cj.jdbc.Driver
    db2:
      url: jdbc:mysql://localhost:3306/xiaotianxian?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
      username: ***
      password: ***
      driver-class-name: com.mysql.cj.jdbc.Driver

数据源配置,有几个数据源配置几份

点击查看代码
package com.example.miniprojectdemo.common.atomiks;

import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import com.mysql.cj.jdbc.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
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.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.StringJoiner;

/**
 * @description:
 * @author: wangqiang
 * @create: 2022-11-21 13:32:29
 */
@Component(value = "db1")
@ConfigurationProperties(prefix = "spring.datasource.db1")
@MapperScan(basePackages = "com.example.miniprojectdemo.dao.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
public class DB1 {
    private String driverClassName;
    private String url;
    private String username;
    private String password;

    @Bean(name = "db1DataSource")
    public DataSource dataSource() throws SQLException {
        MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
        mysqlXADataSource.setUrl(url);
        mysqlXADataSource.setUser(username);
        mysqlXADataSource.setPassword(password);
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);

        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setUniqueResourceName("db1DataSource");
        xaDataSource.setMaxPoolSize(20);
        xaDataSource.setMinPoolSize(3);
        return xaDataSource;
    }


    //配置数据源
    @Bean(name = "db1SqlSessionFactory")
    public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        //mapper.xml 的位置
        Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath*:mappers/db1/*.xml");
        factoryBean.setMapperLocations(resources);
        factoryBean.setDataSource(dataSource);
        factoryBean.setTypeAliasesPackage("com.example.miniprojectdemo.domain");//实体类别名
        //防止pagehelper分页功能失效
        /*MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        factoryBean.setPlugins(new Interceptor[]{interceptor});*/
        return factoryBean.getObject();
    }

    @Bean(name = "db1SqlSessionTemplate")
    public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }




    public DB1() {
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", DB1.class.getSimpleName() + "[", "]")
                .add("driverClassName='" + driverClassName + "'")
                .add("url='" + url + "'")
                .add("username='" + username + "'")
                .add("password='" + password + "'")
                .toString();
    }
}

事务配置,无论几个数据源只需要配置这一个

点击查看代码
package com.example.miniprojectdemo.common.atomiks;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
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 javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

/**
 * @description:
 * @author: wangqiang
 * @create: 2022-11-21 14:03:57
 */
@Configuration
public class TransactionManagerConf {
    /**
     * 不管有多少个数据源只要配置一个 TransactionManager
     */

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

    @Bean(name = "userTransaction")
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(10000);
        return userTransactionImp;
    }

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

使用方式

与普通事务管理相似,在业务方法上加上注解@Transactional,

点击查看代码
    @Transactional
    public void testAtomiksTs(User user){
        user.setOpenId(UUID.randomUUID().toString().replace("-",""));
        int i = userDao.insertDY(user);
        logger.info("执行结果:{}", i);
        user.setOpenId(UUID.randomUUID().toString().replace("-",""));
        int i1 = db2UserDao.insertDY(user);
        logger.info("执行结果:{}", i);
        i = 1/0;
    }

注意事项

oracle可能需要开启权限,若出现如下错误可尝试使用赋予权限解决

grant select on sys.dba_pending_transactions to USER_NAME;
grant select on sys.pending_trans$ to USER_NAME;
grant select on sys.dba_2pc_pending to USER_NAME;
grant execute on sys.dbms_system to USER_NAME