配置自己的mybatis多数据库

目录

简介:

实现的原理:

实现的步骤:

1、在yml文件配置数据库信息,先连接上默认数据库。

2、替换targetDataSources存储的数据源信息(重要)

3、最后一步修改determineCurrentLookupKey()(重要)

4、DatabaseContextHolder类(重要):

ruoyi方法:

masterDataSource方法(不重要)这里就是我们在yml文件中配置生效的原因:

afterPropertiesSet()方法,将targetDataSources内容放入到resolveDataSources中:

配置的默认数据源有什么用:(读懂这个方法就知道上边配置的作用了)


简介:

       这里有一个需求,每个用户都有自己对应的数据库,但是只有一个登录页。这就要求后端判断每一个用户连接,动态的切换操作的数据库,使用户操作自己的数据库。而数据库的信息是保存在一个名为school的表中,这就需要我们先连接上默认数据库,读取school表记录,然后创建相应的数据源。

       这是一个ruoyi项目,基于 spring boot+mybaits pluse+druid+Security 技术的项目。核心原理都是一样的,是不是ruoyi项目是不重要的,只要知道了原理,就可以为所欲为

 school表:其中定义了多个数据源信息:

ps:

  1. https://blog.csdn.net/qq_42892858/article/details/109579451 这是一篇以前写的文章,通过一个简单的案例,讲解了多数据源,但它也是不完整的,可以通过阅读大体了解下配置步骤。
  2. 虽然这是基于ruoyi项目,不过重点是多数据源的配置原理,这也是通用的

实现的原理:

       spring的动态数据源核心类AbstractRoutingDataSource里保存了数据源的连接信息,所以我们想定义适应当前项目的多数据源配置也是去修改spring的这个类。如下图,就是AbstractRoutingDataSource类,可以知道:

  1. determineCurrentLookupKey() 方法能返回要操作数据源的key。而这是一个抽象方法,需要自定义实现。
  2. targetDataSources存放了数据源的定义信息,是一个map对象。可以猜到在访问数据库时先从determineCurrentLookupKey()方法获得key,在通过key来这里取出对应连接
  3. defaultTargetDataSource存储的是默认数据源,
  4. resolvedXxx才是应用实际访问的对象,也就是要将targetXxx数据存储在resolvedXxx内

实现的步骤:

1、在yml文件配置数据库信息,先连接上默认数据库。

因为我们要去默认库中读取多数据源信息。所以先连接上默认数据库。

这里是druid数据源,所以我们自定义的数据源也要实现druid配置(最好的方法是找到druid创建数据源的类,我们去调用,这样我们创建的数据源也就实现了druid定义的参数)

2、替换targetDataSources存储的数据源信息(重要)

@Component   //这是一个组件
public class MultipleDataSources {
    @Autowired   
    DynamicDataSource dynamicDataSource1;  //spring对数据源的管理
    @Autowired  //这是存放数据源信息的表
    private SchoolsService schoolsService;
    @Value("${multipleDatabase.defaultDatabase}")  //这是在yml配置定义的默认数据源
    private String defaultDatabase;

    //driuid参数类,对把配置文件中的链接配置设置到内
    @Autowired
    DruidProperties druidProperties;

    @PostConstruct  //作用是生成bean的时候运行这个方法,也就是在这个方法修改数据源信息
    public void setDynamicDataSource(){
        Map<Object, Object> targetDataSources = new HashMap<>();
        List<Schools> schools = schoolsService.list();
        for (Schools school : schools) {
            //通过creatDruidDataSource方法将school信息生成数据源并放在targetDataSources中
            targetDataSources.put(school.getDbName(),creatDruidDataSource(school));
        }
        //这里只是设置了数据源的定义
        dynamicDataSource1.setTargetDataSources(targetDataSources);
        dynamicDataSource1.setDefaultTargetDataSource(targetDataSources.get(defaultDatabase));
        dynamicDataSource1.afterPropertiesSet();

        //完成新增数据源后,将ruoyi写人数据源  获取 方法停用
        DatabaseContextHolder.defaultDatabase = defaultDatabase;
        dynamicDataSource1.fla = false;
    }


    public DataSource creatDruidDataSource(Schools school){
        //创建druidDataSource
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        if (dataSource.getUsername() == null) {
            dataSource.setUsername(school.getLoginName());
        }
        if (dataSource.getPassword() == null) {
            dataSource.setPassword(school.getLoginPassword());
        }
        if (dataSource.getUrl() == null) {
            dataSource.setUrl(school.getLoginUrlPrefix()+school.getDbName()+school.getLoginUrlSuffix());
        }
        if (dataSource.getDriverClassName() == null) {
            dataSource.setDriverClassName(school.getLoginDriver());
        }
        //给这个数据源设置基础参数  直接调用  这样的配置文件中对druid人配置也会生效
        DruidDataSource druidDataSource = druidProperties.dataSource(dataSource);
        return druidDataSource;
    }
}
  • 2.1spring中有一个容器存放所有bean,所以我们修改也是去修改bean容器里AbstractRoutingDataSource。这里通过@Autowired注解获得bean容器的类。DynamicDataSource实现了AbstractRoutingDataSource,所以实现上修改的是DynamicDataSource类
@Autowired
    DynamicDataSource dynamicDataSource1;
  • 2.2 我们创建的数据源也要是druid数据源,creatDruidDataSource方法将school信息创建数据源并返回。createDruidDataSource方法的写法是跟ruoyi中实现步骤一样的。后文有介绍ruoyi中的masterDataSource方法,有兴趣可以看一下。

  • 2.3核心步骤:AbstractRoutingDataSource类中的targetDataSources存放了数据源的定义信息,defaultTargetDataSource存储了默认数据源。这里就是去将这两个参数设置成我们自定义的。但实际用到的参数是resolvedXxx参数,afterPropertiesSet()方法就是将targetXxx参数转化为resolvedXxx参数。后边有afterProperTiesSet()方法介绍,有兴趣可以看下如何转换的。

dynamicDataSource1.setTargetDataSources(targetDataSources);
        dynamicDataSource1.setDefaultTargetDataSource(targetDataSources.get(defaultDatabase));
        dynamicDataSource1.afterPropertiesSet();

3、最后一步修改determineCurrentLookupKey()(重要)

在原理上说了配置多数据源最重要的就是修改TargetDataSource参数与determineCurrentLookupKey方法,TargetDataSource已经搞定了,现在只要自定义determineCurrentLookupKey()方法,让它返回的key是我们在TargetDataSource中存放的key就可以。所以我们要找到ruoyi中定义的dynamicDataSource()类,去修改它的determineCurrentLookupKey()就好了。可以使用ctrl+shift+f快速找到DynamicDataSource类完成修改。

如下图:fla参数是新加的,注意在setDynamicDataSource方法中,我们配置好TargetDataSource参数后,将fla改成了false。这是因为对数据的修改是项目编译完成之后执行的,所以刚开始是使用yml文件中定义的数据源,所以这里返回的key我们不去修改。而当我们替换成新的数据源时就要使用TargetDataSource中存储的key了。DataBaseContextHolder是我们自定义的,下边有这个这个类的介绍。

4、DatabaseContextHolder类(重要):

TreadLocal对象是一个线程对象,每个线程都有对应的值,也可以说A线程赋值a,B线程赋值b,下次A线程取出来的还是a,B线程取出来的还是b。

在本项目中是这么用的:

  1. 当用户登录前调用DatabaseContestHolder.setDataSourceType(),将contextHolder的值设置成defaultDatabase,这样就会到默认库实现Security中登录逻辑。并且将用户对应的数据库源的key值放入seesion中
  2. 那么当一个用户操作数据前,先将seesion中提取到要操作的数据源key,在将这个key存储到contextHolder中。那么spring通过determineCurrentLookupKey()拿到的key就是这个用户要操作的key了
public class DatabaseContextHolder {
    public   static String defaultDatabase = "";
    private static final ThreadLocal<String> contextHolder = new ThreadLocal();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        String s = contextHolder.get();
        if(s == null || s.equals("") || s.equals("null")){
            return defaultDatabase;
        }
        return s;
    }
}

ruoyi方法:

masterDataSource方法(不重要)这里就是我们在yml文件中配置生效的原因:

 @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        DruidDataSource druidDataSource = druidProperties.dataSource(dataSource);
        return druidDataSource;
    }
  • 可见这是方法作用是生成一个bean,那么肯定会有地方会引入这个bean.往下翻 dataSource()就引用了masterDataSource生成的bean,而dataSource方法生成了DynamicDataSource bean,这个bean就是我们最终要修改的bean.

  • @ConfigurationProperties("spring.datasource.druid.master")  在方法上使用@ConfigurationProperties,会将参数设置到返回的对象中。这里就是设置的url,name,password

  • DruidDataSource druidDataSource = druidProperties.dataSource(dataSource); 就是给drui数据源设置相关的参数。首先知道 druidProperties是一个bean,所有我们去调用这个方法,也要先获得这个bean,在去给我们新建的数据源设置参数。
    

afterPropertiesSet()方法,将targetDataSources内容放入到resolveDataSources中:

配置的默认数据源有什么用:(读懂这个方法就知道上边配置的作用了)

如果因为不了解ruoyi,觉得上边的配置复杂,可以好好读一下这个方法,就能更深入的了解了。可以把这个方法看作和外界唯一的接口,如果要操作数据,就要先通过这个接口获取数据源。甚至可以重写这个方法,让它根据我们的逻辑返回数据源。反正我们只要返回一个数据源就可以了!!!

  • Object lookupKey = this.determineCurrentLookupKey();  得到数据源的key,在第3步我们修改了这个方法
  • DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey); 通过key在resolvedDataSources中得到一个数据源,而resolvedDataSources在第2步也定义了
  • if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
        dataSource = this.resolvedDefaultDataSource;
    }  如果通过key没有拿到dataSource,就使用默认的数据源,在上边也配置默认数据源了
  • 如果通过key没有拿到数据源,也没有配置默认数据源,那只能抛出异常了

 

 


版权声明:本文为qq_42892858原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。