springboot设置多数据源,使用 atomikos 管理多数据库事务

目录

springboot设置多数据源

1.项目结构

2.数据库表结构

3.application.yml

 4.pom文件

5.自定义数据源的配置类

6.实体类

7.dao层

8.service层

 9.controller层

10.测试

使用 atomikos 管理多数据库事务

1.修改MysqlConfig

2.修改OracleConfig

3.添加测试方法

4.测试

更多spring事务相关


springboot设置多数据源

本文使用springboot框架,设置两个数据源(mysql,oracle)。

1.项目结构

2.数据库表结构

两个数据库的结构一样

mysql:

DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp`  (
  `id` int(11) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

INSERT INTO `aaa`.`emp`(`id`, `name`, `remark`) VALUES (1, 'mysql', '我从mysql来');

oracle:

create table EMP
(
  id     NUMBER not null,
  name   VARCHAR2(50),
  remark VARCHAR2(50)
);

insert into emp (ID, NAME, REMARK)
values (1, 'oracle', '我来自oracle');

3.application.yml

server:
  port: 8888

mydatasource:
  first:
    driverClassName: com.mysql.jdbc.Driver
    jdbc-url: jdbc:mysql://127.0.0.1:3306/aaa
    username: root
    password: yuwen
  second:
    driverClassName: oracle.jdbc.driver.OracleDriver
    jdbc-url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
    username: scott
    password: yuwen

 4.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.2.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo-dataSource</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo-dataSource</name>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<!--ojdbc-->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.46</version>
		</dependency>
		<!--druid连接池-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.29</version>
		</dependency>
		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</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-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<jvmArguments>
						-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999
					</jvmArguments>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

5.自定义数据源的配置类

MysqlConfig:定义了mysql数据库的相关配置

package com.example.demo.config;

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.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * className: MysqlConfig <br/>
 * packageName:com.example.demo.config <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:48 <br/>
 */

@Configuration
@MapperScan(basePackages = "com.example.demo.dao.mysql", sqlSessionTemplateRef = "firstSqlSessionTemplate")
public class MysqlConfig {

    @Bean("firstDataSource")
    @ConfigurationProperties(prefix = "mydatasource.first")
    public DataSource firstDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean("firstSqlSessionFactory")
    public SqlSessionFactory firstSqlSessionFactory(@Qualifier("firstDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致)
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/mysql/*.xml"));
        return bean.getObject();
    }

    @Bean("firstSqlSessionTemplate")
    public SqlSessionTemplate firstSqlSessionTemplate(@Qualifier("firstSqlSessionFactory") SqlSessionFactory factory) {
        return new SqlSessionTemplate(factory);
    }

    @Bean("firstDataSourceTransactionManager")
    public DataSourceTransactionManager firstDataSourceTransactionManager(@Qualifier("firstDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}

OracleConfig:定义了oracle数据库的相关配置

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
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.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * className: OracleConfig <br/>
 * packageName:com.example.demo.config <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:48 <br/>
 */

@Configuration
@MapperScan(basePackages = "com.example.demo.dao.oracle", sqlSessionTemplateRef = "secondSqlSessionTemplate")
public class OracleConfig {

    @Value("${mydatasource.second.driverClassName}")
    private String driverClassName;

    @Value("${mydatasource.second.jdbc-url}")
    private String jdbcUrl;

    @Value("${mydatasource.second.username}")
    private String username;

    @Value("${mydatasource.second.password}")
    private String password;

    @Bean("secondDataSource")
    public DataSource secondDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setUrl(jdbcUrl);
        dataSource.setDriverClassName(driverClassName);
        return dataSource;
    }

    @Bean("secondSqlSessionFactory")
    public SqlSessionFactory secondSqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        // mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致)
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/oracle/*.xml"));
        return factoryBean.getObject();
    }

    @Bean("secondSqlSessionTemplate")
    public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean("secondDataSourceTransactionManager")
    public DataSourceTransactionManager secondDataSourceTransactionManager(@Qualifier("secondDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}

6.实体类

package com.example.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * className: Emp <br/>
 * packageName:com.example.demo.entity <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:31 <br/>
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp {

    private int id;

    private String name;

    private String remark;

}

7.dao层

MysqlEmpDao
package com.example.demo.dao.mysql;

import com.example.demo.entity.Emp;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * className: EmpDao <br/>
 * packageName:com.example.demo.dao.mysql <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:34 <br/>
 */

@Repository
public interface MysqlEmpDao {
    List<Emp> findInMysql();
}

 OracleEmpdao

package com.example.demo.dao.oracle;

import com.example.demo.entity.Emp;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * className: Empdao <br/>
 * packageName:com.example.demo.dao.oracle <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:35 <br/>
 */

@Repository
public interface OracleEmpdao {

    List<Emp> findInOracle();
}

 mysql的dao对应的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" >
<!-- 映射文件,映射到对应的SQL接口 -->
<mapper namespace="com.example.demo.dao.mysql.MysqlEmpDao">

    <select id="findInMysql" resultType="com.example.demo.entity.Emp">
        SELECT * FROM emp
    </select>

</mapper>

oracle的dao对应的xmk

<?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" >
<!-- 映射文件,映射到对应的SQL接口 -->
<mapper namespace="com.example.demo.dao.oracle.OracleEmpdao">

    <select id="findInOracle" resultType="com.example.demo.entity.Emp">
        SELECT * FROM emp
    </select>

</mapper>

8.service层

接口

package com.example.demo.service;

import com.example.demo.entity.Emp;

import java.util.List;

/**
 * className: EmpService <br/>
 * packageName:com.example.demo.service <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:32 <br/>
 */
public interface EmpService {

    /**
     * 查询mysql
     *
     * @return
     */
    List<Emp> findInMysql();

    /**
     * 查询oracle
     *
     * @return
     */
    List<Emp> findInOracle();
}

实现类 

package com.example.demo.service.impl;

import com.example.demo.dao.mysql.MysqlEmpDao;
import com.example.demo.dao.oracle.OracleEmpdao;
import com.example.demo.entity.Emp;
import com.example.demo.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * className: EmpServiceImpl <br/>
 * packageName:com.example.demo.service.impl <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:32 <br/>
 */

@Service
@Slf4j
public class EmpServiceImpl implements EmpService {

    @Autowired
    private MysqlEmpDao mysqlEmpDao;

    @Autowired
    private OracleEmpdao oracleEmpdao;

    @Override
    public List<Emp> findInMysql() {
        return mysqlEmpDao.findInMysql();
    }

    @Override
    public List<Emp> findInOracle() {
        return oracleEmpdao.findInOracle();
    }
}

 9.controller层

package com.example.demo.controller;

import com.example.demo.entity.Emp;
import com.example.demo.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * className: TestController <br/>
 * packageName:com.example.demo.controller <br/>
 * description:  <br/>
 *
 * @author yuwen <br/>
 * @date: 2020-6-30 21:24 <br/>
 */

@RestController
@Slf4j
public class TestController {

    @Autowired
    private EmpService empService;

    @GetMapping("/findInMysql")
    public List<Emp> findInMysql() {
        return empService.findInMysql();
    }

    @GetMapping("/findInOracle")
    public List<Emp> findInOracle() {
        return empService.findInOracle();
    }
}

10.测试

 启动项目,

访问  http://localhost:8888/findInMysql   出现   [{"id":1,"name":"mysql","remark":"我从mysql来"}]

访问  http://localhost:8888/findInOracle  出现   [{"id":1,"name":"oracle","remark":"我从oracle来"}]

 

至此,整合多数据源成功。

 

使用 atomikos 管理多数据库事务

多数据源可以正常使用后,接下来该思考事务问题。

针对单个数据源,在service层的方法上加上 @Transactional ,设置 transactionManager 属性后(对应数据源的事务管理器),即可进行事务控制。

可是如果事务跨库了呢,例如在 mysql 和 oracle 分别插入一条数据,在执行 mysql 的新增成功了,执行 oracle 的新增失败了,那么 mysql的事务会回滚吗?答案是不可以

怎么才能实现跨数据库的事务呢?

百度得知使用 atomikos  可以进行多数据库的事务管理,以下为调研代码。

1.添加依赖

<!--atomikos-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jta-atomikos</artifactId>
		</dependency>

2.修改MysqlConfig和OracleConfig 

MysqlConfig

package com.example.demo.config;

import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
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.beans.factory.annotation.Value;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * className: MysqlConfig <br/>
 * packageName:com.example.demo.config <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:48 <br/>
 */

@Configuration
@MapperScan(basePackages = "com.example.demo.dao.mysql", sqlSessionTemplateRef = "firstSqlSessionTemplate")
public class MysqlConfig {

    @Value("${mydatasource.first.jdbc-url}")
    private String jdbcUrl;

    @Value("${mydatasource.first.username}")
    private String username;

    @Value("${mydatasource.first.password}")
    private String password;

    @Bean("firstDataSource")
    public DataSource firstDataSource() {
        // 创建MYsql实现XA规范的分布式数据源
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        // 设置连接信息
        mysqlXaDataSource.setUrl(jdbcUrl);
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(password);
        mysqlXaDataSource.setUser(username);
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        // 数据源改为Atomikos,将事务交给Atomikos统一管理
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("firstDataSource");
        return xaDataSource;
    }

    @Bean("firstSqlSessionFactory")
    public SqlSessionFactory firstSqlSessionFactory(@Qualifier("firstDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致)
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/mysql/*.xml"));
        return bean.getObject();
    }

    @Bean("firstSqlSessionTemplate")
    public SqlSessionTemplate firstSqlSessionTemplate(@Qualifier("firstSqlSessionFactory") SqlSessionFactory factory) {
        return new SqlSessionTemplate(factory);
    }
//
//    @Bean("firstDataSourceTransactionManager")
//    public DataSourceTransactionManager firstDataSourceTransactionManager(@Qualifier("firstDataSource") DataSource dataSource) {
//        return new DataSourceTransactionManager(dataSource);
//    }

}

OracleConfig 

package com.example.demo.config;

import oracle.jdbc.xa.client.OracleXADataSource;
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.beans.factory.annotation.Value;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

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

/**
 * className: OracleConfig <br/>
 * packageName:com.example.demo.config <br/>
 * description:  <br/>
 *
 * @date: 2020-10-20 20:48 <br/>
 */

@Configuration
@MapperScan(basePackages = "com.example.demo.dao.oracle", sqlSessionTemplateRef = "secondSqlSessionTemplate")
public class OracleConfig {

    @Value("${mydatasource.second.jdbc-url}")
    private String jdbcUrl;

    @Value("${mydatasource.second.username}")
    private String username;

    @Value("${mydatasource.second.password}")
    private String password;

    @Bean("secondDataSource")
    public DataSource secondDataSource() throws SQLException {
        OracleXADataSource mysqlXaDataSource = new OracleXADataSource();
        mysqlXaDataSource.setURL(jdbcUrl);
        mysqlXaDataSource.setPassword(password);
        mysqlXaDataSource.setUser(username);

        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("secondDataSource");
        return xaDataSource;
    }

    @Bean("secondSqlSessionFactory")
    public SqlSessionFactory secondSqlSessionFactory(@Qualifier("secondDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        // mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致)
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/oracle/*.xml"));
        return factoryBean.getObject();
    }

    @Bean("secondSqlSessionTemplate")
    public SqlSessionTemplate secondSqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

//    @Bean("secondDataSourceTransactionManager")
//    public DataSourceTransactionManager secondDataSourceTransactionManager(@Qualifier("secondDataSource") DataSource dataSource) {
//        return new DataSourceTransactionManager(dataSource);
//    }

}

3.添加测试方法

TestController:

/**
     * 单个库事务(mysql)
     *
     * @return
     */
    @GetMapping("/mysqlTrans")
    public Map<String, String> mysqlTrans() {
        Map<String, String> ret = new HashMap<>();
        try {
            empService.mysqlTrans();
            ret.put("stats", "true");
        } catch (Exception e) {
            log.error("TestController mysqlTrans failed", e);
            ret.put("stats", "false");
        }
        return ret;
    }

    /**
     * 单个库事务(oracle)
     *
     * @return
     */
    @GetMapping("/oracleTrans")
    public Map<String, String> oracleTrans() {
        Map<String, String> ret = new HashMap<>();
        try {
            empService.oracleTrans();
            ret.put("stats", "true");
        } catch (Exception e) {
            log.error("TestController oracleTrans failed", e);
            ret.put("stats", "false");
        }
        return ret;
    }

    /**
     * 两个库事务
     *
     * @return
     */
    @GetMapping("/twoTrans")
    public Map<String, String> twoTrans() {
        Map<String, String> ret = new HashMap<>();
        try {
            empService.twoTrans();
            ret.put("stats", "true");
        } catch (Exception e) {
            log.error("TestController twoTrans failed", e);
            ret.put("stats", "false");
        }
        return ret;
    }

service层:

 void oracleTrans();

    void mysqlTrans();

    void twoTrans();

 

@Override
    @Transactional(rollbackFor = Exception.class)
    public void mysqlTrans() {
        int id = (int) (Math.random() * 100);
        Emp emp = new Emp(id, "mysql", "yes");
        mysqlEmpDao.insert(emp);
        log.info("EmpServiceImpl mysqlTrans insert success");

        System.out.println(1 / 0); // 发生异常
        int id2 = (int) (Math.random() * 100);
        Emp emp2 = new Emp(id2, "mysql", "yes");
        mysqlEmpDao.insert(emp2);
        log.info("EmpServiceImpl mysqlTrans insert success");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void oracleTrans() {
        int id = (int) (Math.random() * 100);
        Emp emp = new Emp(id, "oracle", "yes");
        oracleEmpdao.insert(emp);
        log.info("EmpServiceImpl oracleTrans insert success");

        System.out.println(1 / 0); // 发生异常
        int id2 = (int) (Math.random() * 100);
        Emp emp2 = new Emp(id2, "oracle", "yes");
        oracleEmpdao.insert(emp2);
        log.info("EmpServiceImpl oracleTrans insert success");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void twoTrans() {
        int id = (int) (Math.random() * 100);
        Emp emp = new Emp(id, "oracle", "yes");
        mysqlEmpDao.insert(emp);
        log.info("EmpServiceImpl twoTrans insert success");

        int id2 = (int) (Math.random() * 100);
        Emp emp2 = new Emp(id2, "oracle", "yes");
        oracleEmpdao.insert(emp2);
        log.info("EmpServiceImpl twoTrans insert success");
        System.out.println(1 / 0); // 发生异常
    }

4.测试

启动项目,如果报错:javax.transaction.xa.XAException: null

使用 dba账号 通过以下命令给数据源的用户授予XA权限

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

参考:https://blog.csdn.net/qq_43601813/article/details/107248968

启动成功后,通过以下地址进行测试

http://localhost:8888/mysqlTrans

http://localhost:8888/oracleTrans

http://localhost:8888/twoTrans

分析页面结果,结果符合预期,atomikos 可以实现多数据库事务

 

 

更多spring事务相关

http://codin.im/2017/06/07/distributed-transactions-in-spring--with-and-without-xa-translate/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值