springboot 集成多数据源 二 通过继承AbstractRoutingDataSource来实现多数据源


一  实现原理

springboot从出生的时候,就考虑到多数据源的连接支持,其内置就提供了多数据源的抽象类AbstractRoutingDataSource,能过分析一些成熟的第三方多数据源开源组件,其实底层实现的也是或者类似这个原生的 AbstractRoutingDataSource的原理。本人就继续对中实现的非常粗糙的自定义多数据源类进行改造优化,来实现第二个版本。

二  实现细节

AbstractRoutingDataSource 实现数据源切换的原理是:

1. 在AbstractRoutingDataSource中,维护着三个重要的对象  

TargetDataSources(我们设置的多个数据源)

resolvedDataSources(通过TargetDataSources生成的最终用于数据源切换的数据源列表,以键字对的形式存在)

DefaultTargetDataSource  默认使用的数据源是哪个

LookupKey,类似于我们上一篇中自定的ThreadLocal变量,用于查找最终切换的数据源的key

2. 用户发起请求,调用接口,通过LookupKey变量,通过 resolvedDataSources找到最终使用的  DeterminedTargetDataSource,通过获取到的这个ds的getconnection()方法,从而进行数据源的切换。

3. ORM通过获取到的dataSource进行数据库操作

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 使用继承自  AbstractRoutingDataSource 来实现多数据源
 *
 * 缺点:硬编码过多  只做为参考使用
 */
@Component
@Primary
public class MyDynamicDataSourceTwo extends AbstractRoutingDataSource {

    /**用于全局切换数据源的标识**/
    public static ThreadLocal name = new ThreadLocal<>();

    /**
     * 注入我们准备好的两个ds,准备进行数据源切换使用
     *
     * @return
     * @throws SQLException
     */
    @Autowired
    private DataSource ds1;
    @Autowired
    private DataSource ds2;

    /**
     * 这一部是返回数据查找时的键值
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println("切换数据库为:"+CommonConstant.ds_LookupKey.get());
        return CommonConstant.ds_LookupKey.get();
    }

    @Override
    public void afterPropertiesSet() {
        //设置 TargetDataSources
        Map targetDataSources=new HashMap<>();
        targetDataSources.put("w",ds1);
        targetDataSources.put("r",ds2);

        super.setTargetDataSources(targetDataSources);

        //设置默认的数据源
        super.setDefaultTargetDataSource(ds1);

        //调用父类方法,从而初始化最终用于数据源切换的resolvedDataSources
        super.afterPropertiesSet();
    }
}

需要注意的是:重写父类的afterPropertiesSet方法后,我们一定要调用  super.afterPropertiesSet(); 否则无法生成最终用于数据源切换的。这里我们自定义的数据源主要是做两个操作,第一,设置切换的标识符;第二是设备必要的 AbstractRoutingDataSource里面的三大对象即可,然后调用父类的方法进行参数的初始化。 

三  代码测试。

@RestController
@RequestMapping("user")
public class UserController  {
    @Autowired
    UserService userServer;

    @PostMapping("/new")
    public JsonVo addUser()
    {
        CommonConstant.ds_LookupKey.set("w");
        User user=new User().builder()
                .name(String.valueOf(new Random().nextInt(1000)))
                .money(new Random().nextInt())
                .createTime(new Date())
                .build();
        userServer.addUser(user);
        return JsonVo.builder().code(0).data(user).build();
    }

    @PostMapping("/list")
    public JsonVo list()
    {
        CommonConstant.ds_LookupKey.set("r");
        List userList= userServer.getList();
        return JsonVo.builder().code(0).dataList(userList).build();
    }
}

可以看出,我们只是精简和优化了自定义的数据源,对于程序中的硬切换标识符,暂时还是没有办法拿到,后面一篇,我们通过mybatis插件的方式,拿掉这个硬编码的标识符。


说明:
本代码思想逻辑以及配图,均来自对大师视频的学习,通过自己的总结而来,本人并无视频中的提到的课件,以下代码均为自己所写。配图如有侵权,请联系删除。
原学习视频地址: https://www.bilibili.com/video/BV1YT4y1X7v7?p=3&spm_id_from=pageDriver
最后感谢大师无私奉献学习视频,至敬大师的作品。请有学习时间的同学,可以观看原大师的作品。