Appearance
多数据源
MyBaits-Flex 内置了功能完善的多数据源支持^1.0.6,不需要借助第三方插件或者依赖,开箱即用, 支持包括 druid、hikaricp、dbcp2、beecp 在内的任何数据源,MyBatis-Flex 多数据源配置如下:
yaml
mybatis-flex:
datasource:
ds1:
url: jdbc:mysql://127.0.0.1:3306/db
username: root
password: 123456
ds2:
url: jdbc:mysql://127.0.0.1:3306/db2
username: root
password: 123456在以上配置中,ds1 和 ds2 是由用户自定义的,我们可以理解为数据源的名称,或者数据源的 key,这个在动态切换数据库中非常有用。
在无 Spring 或 Solon 框架的场景下,代码如下:
java
DataSource dataSource1 = new HikariDataSource();
dataSource1.setJdbcUrl(....)
DataSource dataSource2 = new HikariDataSource();
dataSource2.setJdbcUrl(....)
DataSource dataSource3 = new HikariDataSource();
dataSource3.setJdbcUrl(....)
MybatisFlexBootstrap.getInstance()
.setDataSource("ds1", dataSource1)
.addDataSource("ds2", dataSource2)
.addDataSource("ds3", dataSource3)
.start();开始使用
默认使用第一个配置的数据源:
java
List<Row> rows = Db.selectAll("tb_account");
System.out.println(rows);通过编码的方式,切换到数据源 ds2:
java
try{
DataSourceKey.use("ds2")
List<Row> rows = Db.selectAll("tb_account");
System.out.println(rows);
}finally{
DataSourceKey.clear();
}或者
java
List<Row> rows = DataSourceKey.use("ds2"
, () -> Db.selectAll("tb_account"));数据源切换(设置)
MyBatis-Flex 提供了 4 种方式来配置数据源:
- 1、编码,使用
DataSourceKey.use方法。 - 2、
@UseDataSource("dataSourceName")在 Mapper 类上,添加注解,用于指定使用哪个数据源。 - 3、
@UseDataSource("dataSourceName")在 Mapper 方法上,添加注解,用于指定使用哪个数据源。 - 4、
@Table(dataSource="dataSourceName")在 Entity 类上添加注解,该 Entity 的增删改查请求默认使用该数据源。
在 SpringBoot 或 Solon 项目上,
@UseDataSource("dataSourceName")也可用于在 Controller 或者 Service 类上。若是 Spring 项目(非 SpringBoot), 用户需要参考MultiDataSourceAutoConfiguration进行配置后才能使用。
DataSourceKey.use 示例:
java
try{
DataSourceKey.use("ds2")
List<Row> rows = Db.selectAll("tb_account");
System.out.println(rows);
}finally{
DataSourceKey.clear();
}@UseDataSource("dataSourceName") 示例:
java
@UseDataSource("ds2")
interface AccountMapper extends BaseMapper{
List<Account> myMethod();
}或者
java
interface AccountMapper extends BaseMapper{
@UseDataSource("ds2")
List<Account> myMethod();
}@UseDataSource(value = "#my_expression")扩展数据源切换,表达式动态读取 key 值
- 自定义表达式扩展,对接口 DataSourceProcessor 进行实现,推荐约定动态表达式以
#作为起始标识(当然并不强制做要求)
java
// 自定义基于Session的表达式取值示例
public class InSessionDataSourceProcessor implements DataSourceProcessor {
private static final String SESSION_PREFIX = "#session";
@Override
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
if (StrUtil.isBlank(dataSourceKey)) return null;
if (!dataSourceKey.startsWith(SESSION_PREFIX)) return null;
HttpServletRequest request = RequestContextHolder.get.....;
if (null==request) return null;
return request.getSession().getAttribute(dataSourceKey.substring(9)).toString();
}
}- 内置取值表达式取值解析处理器,作为示例参考
DelegatingDataSourceProcessor,对DataSourceProcessor结构进行扩大和增强的委托类,多处理器推荐使用该委托类进行注入,注意委托类内使用List委托管理多个解析处理器,执行时有先后顺序。ParamIndexDataSourceProcessor,针对简单参数快速取值。SpelExpressionDataSourceProcessor,Spring模式下SPEL表达式取值。
- 注入动态数据源取值处理器
java
// 注入顺序既为执行时的先后顺序(前面的处理器一旦正确处理,将不会再往下传递处理)
DelegatingDataSourceProcessor dataSourceProcessor = DelegatingDataSourceProcessor.with(
// 参数索引快速取值的
new ParamIndexDataSourceProcessor(),
// Spel 表达式的
new SpelExpressionDataSourceProcessor(),
new .....等多个自行扩展表达式()
);
// 实例 setter 赋值
DataSourceManager.setDataSourceProcessor(dataSourceProcessor);- 应用示例:
java
@Mapper
public interface MyMapper extends BaseMapper<MyEntity >{
// 取值第一个参数的值(arg0的值)
@UseDataSource(value = "#first")
MyEntity testFirst(String arg0, String arg1, String arg2);
// 取值索引1(第二个参数 arg1 )的值
@UseDataSource(value = "#index1")
MyEntity testIndex(String arg0, String arg1, String arg2);
// 取值最后一个参数的值(arg3的值)
@UseDataSource(value = "#last")
MyEntity testLast(String arg0, String arg1, String arg2, String arg3);
// Spel 表达式取值数据源
@UseDataSource(value = "#condition.getDsId()")
MyEntity testSpel(MyDatasourceCondition condition);
}- 调用
java
public void test(){
// mapper中的注解 @UseDataSource(value = "#first")
myMapper.testFirst("my-dskey1", "arg1", "arg2");
// 取值索引1(第二个参数)的值;mapper中的注解 @UseDataSource(value = "#index1")
myMapper.testIndex("arg0", "my-dskey2", "arg2");
// mapper中的注解 @UseDataSource(value = "#last")
myMapper.testLast("arg0", "arg1", "arg2","my-dskey4");
MyDatasourceCondition condition = new MyDatasourceCondition();
condition.setDsId("my-dskey5");
// mapper中的注解 @UseDataSource(value = "#condition.getDsId()")
myMapper.testSpel(condition);
}- 注意:
- 1:使用区分
Spring模式和非Spring模式,Spring模式下,处理逻辑DataSourceInterceptor优先级高于FlexMapperProxy, 所以Spring模式下仅 DataSourceInterceptor 生效(切面生效的前提下)。非Spring模式下,仅支持注解使用到 Mapper(Dao层), 使用到其他层(如Service层)不支持注解解析。 - 2:
Spring模式下,不区分使用到程序的层(Controller、Service、Dao层都支持),下层控制粒度细上层控制粒粗,使用时根据需要进行灵活应用。
- 1:使用区分
@Table(dataSource="dataSourceName") 示例:
java
@Table(value = "tb_account", dataSource = "ds2")
public class Account {
@Id(keyType = KeyType.Auto)
private Long id;
private String userName;
}数据源配置的优先级
DataSourceKey.use() > @UseDataSource()在方法上 > @UseDataSource()在类上 >@Table(dataSource="...")
数据源缺失处理器
当无法根据 dataSourceKey 找到数据源时,默认情况下会抛出 IllegalStateException 异常。 数据源缺失处理器(DataSourceMissingHandler)提供了更加灵活的处理方式,你可以通过它自定义处理逻辑(如:记录日志、抛出异常或主动初始化新的数据源)。
使用示例
我们推荐使用 MyBatisFlexCustomizer 来配置数据源缺失处理器,如下所示:
java
@Configuration
public class MyBatisFlexConfiguration implements MyBatisFlexCustomizer {
@Override
public void customize(FlexGlobalConfig globalConfig) {
// ...
// 配置数据源缺失处理器:此处演示的是以后备逻辑主动初始化数据源
globalConfig.setDataSourceMissingHandler((dataSourceKey, dataSourceMap) -> {
// 根据 key 获取数据源
DataSource ds = customCreateDataSource(dataSourceKey);
// 取不到的时候返回 null,后续代码逻辑仍然由 FlexDataSource 处理(即抛出异常)
if (ds == null) return null;
// 添加新的数据源,避免下次再次触发和创建
dataSourceMap.put(dataSourceKey, ds);
return dataSourceMap;
});
// ...
}
}更多的 Spring 或 Solon Yaml 配置支持
yaml
mybatis-flex:
datasource:
ds1:
url: jdbc:mysql://127.0.0.1:3306/db
username: root
password: 123456
ds2:
url: jdbc:mysql://127.0.0.1:3306/db2
username: root
password: 123456在以上配置中,ds1 和 ds2 并未指定使用哪个数据连接池,MyBatis-Flex 会 自动探测 当前项目依赖了哪个连接池。 目前支持了 druid、hikaricp、dbcp2、beecp 的自动探测,如果项目中使用的不是这 4 种类型只有,需要添加 type 配置,如下所示:
yaml
mybatis-flex:
datasource:
ds1:
type: com.your.datasource.type1
url: jdbc:mysql://127.0.0.1:3306/db
username: root
password: 123456
ds2:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/db2
username: root
password: 123456同时,项目若使用到了多个数据源类型,则也需要添加 type 来指定当前数据源的类型。
除了 type、url、username、password 的配置以外,MyBatis-Flex 支持该 DataSource 类型的所有参数配置, 例如,在 DruidDataSource 类中存在 setAsyncInit 方法,我们就可以添加 asyncInit 的配置,如下所示:
yaml
mybatis-flex:
datasource:
ds1:
type: druid
url: jdbc:mysql://127.0.0.1:3306/db
username: root
password: 123456
asyncInit: true
ds2:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/db2
username: root
password: 123456因此,只要该 DataSource 有 setter 方法,我们就可以在配置文件中进行配。与此相反的是:若该 DataSource 类型没有该属性,则不能使用这个配置。
提示
在数据源的配置中,type 可以配置为某个 DataSource 的类名,也可以配置为别名,别名支持有:druid、 hikari、hikaricp、bee、beecp、dbcp、dbcp2。
动态添加新的数据源
在多租户等某些场景下,我们可能需要用到动态的添加新的数据源,此时可以通过如下的方式进行添加。
java
FlexDataSource flexDataSource = FlexGlobalConfig.getDefaultConfig()
.getDataSource();
//新的数据源
HikariDataSource newDataSource = new HikariDataSource();
flexDataSource.addDataSource("newKey", newDataSource);需要注意的是: 通过 FlexGlobalConfig 去获取 FlexDataSource 时,需等待应用完全启动成功后,才能正常获取 FlexDataSource, 否则将会得到 null 值。
Spring 用户可以通过 ApplicationListener 去监听 ContextRefreshedEvent 事件,然后再去添加新的数据源,如下代码所示:
java
public class DataSourceInitListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FlexDataSource dataSource = FlexGlobalConfig.getDefaultConfig()
.getDataSource();
dataSource.addDataSource("....", new DruidDataSource("..."));
}
}多数据源负载均衡 ^1.5.4
数据源负载均衡指的是:在进行数据查询的时候,随机使用一个数据源。 这是的在高并发的场景下,起到负载的效果。
假设多数据源配置如下:
yaml
mybatis-flex:
datasource:
ds1:
type: druid
url: jdbc:mysql://127.0.0.1:3306/db
username: root
password: 123456
asyncInit: true
ds2:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/db2
username: root
password: 123456
other:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/db3
username: root
password: 123456以上配置了三个数据源,分别为:ds1、ds2、other,假设我们想负载均衡使用 ds1、ds2 ,那么代码如下:
java
try{
DataSourceKey.use("ds*");
List<Row> rows = Db.selectAll("tb_account");
System.out.println(rows);
}finally{
DataSourceKey.clear();
}DataSourceKey.use("ds*") 中的 ds* 指的是使用 ds 开头的任意一个数据源。ds* 必须以 "*" 结尾, 中间不能有空格,比如 "ds *" 中间有空格是不行的。

