Solon v3.3.0

数据源之“多个数据源”

</> markdown

数据源是个象抽的接口,可以各种不同方式的玩转,容易混乱(尤其是"多数据源"和"动态数据源")。需要一些约定,把概念固定下来。

1、三个数据源概念约定

以数据源在“容器”里的记录为基准。设计这个概念约定。

概念约定
数据源在容器里,会产生一个数据源的 Bean 记录。可以通过容器获取。
多个数据源
(或多数据源)
在容器里,会产生多个数据源的 Bean 记录。可以通过容器获取。
子数据源在一个数据源内部,还有二级数据源(也叫子数据源)。子数据源,不会在容器里有 Bean 记录。

2、其它数据源概念

分片数据源(内部有子数据源,基于分片规则切换):

  • 是指一个数据源内有多个子数据源,根据规则确定相关数据源。一般用于分库分表或读写分离场景等。
  • 比如:ShardingDataSource(基于 Apache ShardingSphere 适配的数据源)

动态数据源(内部有子数据源,基于线程状态切换):

  • 是指一个数据源内有多个子数据源,且可以动态切换内部的子数据源。用时,需要不断手动指定。
  • 比如:DynamicDataSource,参考插件 solon-data-dynamicds
  • 一般通过:@DynamicDs("db_user_1")@DynamicDs("db_user_2") 切换动态数据源内部的子数据源
  • 一个应用里,只能有一个同类型的动态数据源!(否则,线程状态切换就错乱了)

3、“多数据源” 和 “动态数据源” 的区别

从技术上看 “分片数据源”、“动态数据源” 似乎也算 “多数据源”,内部都有多个子数据源。但,我们这里的 “多数据源” 以容器里的记录为准。

  • 多数据源(或多个数据源)

是指在“容器”里有多个数据源的记录

  • 动态数据源

动态数据源,在“容器”里只会有一个数据源的记录。但内部会有多个子数据源。一般,可以通过线程状态进行切换。

4、数据源的获取方式

获取方式:

方式示例代码
注入方式@Inject("db_order")
同步获取方式Solon.context().getBean("db_order")
异步获取方式Solon.context().getBeanAsync("db_order", ds->{ ... })
工具获取方式DsUtils.observeDs(Solon.context(), "db_order", dsWrap->{ ... })

获取子数据源的方式(以 DynamicDataSource 为例):

//选获取数据源
DynamicDataSource dds = Solon.context().getBean("db_user");

//再获取数据源内的子数据源
DataSource tmp = dds.getTargetDataSource("db_user_w");

获取默认数据源的方式:

方式示例代码(按类型获取)
注入方式@Inject
同步获取方式Solon.context().getBean(DataSource.class)
异步获取方式Solon.context().getBeanAsync(DataSource.class, ds->{ ... })
工具获取方式DsUtils.observeDs(Solon.context(), "", dsWrap->{ ... })

5、获取一批数据源

//1.注入方式
@Inject 
Map<String, DataSource> dsMap;

//2.订阅获取方式(可以源源不断,实时获取新建构的数据源)
Solon.context().subWrapsOfType(DataSource.class, dsWrap->{
    //dsWrap.name();   //数据源名字
    //dsWrap.typed();  //是否申明以类型注册的(相当于默认)
    //dsWrap.raw();    //原始 DataSource 实例
});

//3.同步获取方式(要注意时机点)
Map<String, DataSource> dsMap = Solon.context().getBeansMapOfType(DataSource.class);

应用开发时,一般不会直接使用数据源对象,而是使用 ORM 的特定对象及增强注解。

6、插件适配指南(如何获取数据源)

工具 DsUtils.observeDs

/**
* @param dsName 数据源名(为空时,表过默认数据源)
*/
DsUtils.observeDs(appContext, dsName, (dsWrap) -> {
      
});

应用示例:

public class DbBeanInjectorImpl implements BeanInjector<Db> {
    @Override
    public void doInject(VarHolder vh, Db anno) {
        //要求必须注入
        vh.required(true); 
        
        //根据注解获取数据源
        DsUtils.observeDs(vh.context(), anno.value(), (dsWrap) -> {
            inject0(vh, dsWrap);
        });
    }

    private void inject0(VarHolder vh, BeanWrap dsBw) {
        //...
    }
}