SpringBoot+Mybatis配置多数据源及事务方案

本文详细介绍了SpringBoot结合Mybatis配置多数据源的步骤,包括数据库连接、DataSource配置、路由键设置及测试。同时,文章讨论了在多数据源下事务模式无法切换数据源的问题,并分析了原因,提出了解决方案。此外,还探讨了基于XA协议的分布式事务在多数据源场景中的应用,以及其优缺点和适用场景。
摘要由CSDN通过智能技术生成

前言

可能由于业务上的某些需求,我们的系统中有时往往要连接多个数据库,这就产生了多数据源问题。

多数据源的情况下,一般我们要做到可以自动切换,此时会涉及到事务注解 Transactional 不生效问题和分布式事务问题。

关于多数据源方案,笔者在网上看过一些例子,然而大部分都是错误示例,根本跑不通,或者没办法兼容事务。

今天,我们就一点点来分析这些问题产生的根源和相应的解决方法。

一、多数据源

为了剧情的顺利开展,我们模拟的业务是创建订单和扣减库存。

所以,我们先创建订单表和库存表。注意,把他们分别放到两个数据库中。

CREATE TABLE `t_storage` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `t_order` (
  `id` bigint(16) NOT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0',
  `amount` double(14,2) DEFAULT '0.00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1、数据库连接

通过YML文件先把两个数据库都配置一下。

spring:
  datasource:
    ds1:
      jdbc_url: jdbc:mysql://127.0.0.1:3306/db1
      username: root
      password: root
    ds2:
      jdbc_url: jdbc:mysql://127.0.0.1:3306/db2
      username: root
      password: root
2、配置DataSource

我们知道,Mybatis执行一条SQL语句的时候,需要先获取一个Connection。这时候,就交由Spring管理器到DataSource中获取连接。

Spring中有个具有路由功能的DataSource,它可以通过查找键调用不同的数据源,这就是AbstractRoutingDataSource

public abstract class AbstractRoutingDataSource{
    //数据源的集合
    @Nullable
    private Map<Object, Object> targetDataSources;
    //默认的数据源
    @Nullable
    private Object defaultTargetDataSource;
	
    //返回当前的路由键,根据该值返回不同的数据源
    @Nullable
    protected abstract Object determineCurrentLookupKey();
    
    //确定一个数据源
    protected DataSource determineTargetDataSource() {
        //抽象方法 返回一个路由键
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.targetDataSources.get(lookupKey);
        return dataSource;
    }
}

可以看到,该抽象类的核心就是先设置多个数据源到Map集合中,然后根据Key可以获取不同的数据源。

那么,我们就可以重写这个determineCurrentLookupKey方法,它返回的是一个数据源的名称。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();
        return dataBaseType;
    }
}

然后还需要一个工具类,来保存当前线程的数据源类型。

public class DataSourceType {

    public enum DataBaseType {
        ds1, ds2
    }
    // 使用ThreadLocal保证线程安全
    private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();
    // 往当前线程里设置数据源类型
    public static void setDataBaseType(DataBaseType dataBaseType) {
        if (dataBaseType == null) {
            throw new NullPointerException();
        }
        TYPE.set(dataBaseType);
    }
    // 获取数据源类型
    public static DataBaseType getDataBaseType() {
        DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.ds1 : TYPE.get();
        return dataBaseType;
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值