```xml
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-data-dynamicds</artifactId>
</dependency>
```

#### 1、描述

数据扩展插件，为 Solon Data 提供了 **动态数据源** 的能力扩展。（v1.11.1 之后支持）

动态数据源，常见的合适场景：

* 主从数据架构
* 互备数据架构
* 读写分离数据架构

提醒：

* 动态数据源的内部切换是借用 ThreadLocal 实现
* <mark>一个应用里，只能有一个同类型的动态数据源！（否则，线程状态切换就错乱了）</mark>
* 有更丰富的需求时，可基于 solon-data::AbstractRoutingDataSource 自己定制
* <del>与事务注解一起使用时，会失效！！！</del>（v2.8.0 后，支持事务注解管理）

#### 2、数据源构建

* 使用配置构建数据源（具体参考：[《数据源的配置与构建》](/article/794)）

```yaml
# db_order 为固定数据源
solon.dataSources.db_order:
  class: "com.zaxxer.hikari.HikariDataSource"
  jdbcUrl: jdbc:mysql://localhost:3306/db_order?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
  driverClassName: com.mysql.cj.jdbc.Driver
  username: root
  password: 123456

# db_user 为动态数据源（主菜是这里!!!）
solon.dataSources.db_user:
  class: "org.noear.solon.data.dynamicds.DynamicDataSource"
  strict: true #严格模式（指定的源不存时：严格模式会抛异常；非严格模式用默认源）
  default: "db_user_1" #指定默认数据源
  db_user_1: 
    dataSourceClassName: "com.zaxxer.hikari.HikariDataSource"
    jdbcUrl: jdbc:mysql://localhost:3306/db_user?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
    driverClassName: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
  db_user_2:
    dataSourceClassName: "com.zaxxer.hikari.HikariDataSource"
    jdbcUrl: jdbc:mysql://localhost:3307/db_user?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=true
    driverClassName: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
```


* 使用代码构建数据源

```java
//配置数据源 bean
@Configuration
public class Config {
    
    //动态数据源（手动构建）//只是示例一下
    //@Bean("db_user")
    public DataSource dsUser2(@Inject("${demo.ds.db_user}") Properties props) {
        //手动构建，可以不用配置：type, strict
        Map<String, DataSource> dsMap = DsUtils.buildDsMap(props, HikariDataSource.class);
        DataSource dsDef = dsMap.get("default");
        
        DynamicDataSource tmp = new DynamicDataSource();
        tmp.setStrict(true);
        tmp.setTargetDataSources(dsMap);
        tmp.setDefaultTargetDataSource(dsDef);
        
        return tmp;
    }
}
```

#### 3、“注解” 或 “手动” 切换动态数据源


```java
@Component
public class UserService{
    @Db("db_order")
    OrderMapper orderMapper;
    
    @Db("db_user")
    UserMapper userMapper;
    
    @DynamicDs //使用 db_user 动态源内的 默认源
    public void addUser(){
        userMapper.inserUser();
    }
    
    //注解设置二级源
    @DynamicDs("db_user_1") //使用 db_user 动态源内的 db_user_1 源
    public void getUserList(){
        userMapper.selectUserList();
    }
    
    public void getUserList2(){
        //手动设置二级源
        DynamicDsKey.setCurrent("db_user_2"); //使用 db_user 动态源内的 db_user_2 源
        try {
            userMapper.selectUserList();
        } finally {
            DynamicDsKey.remove();
        }
    }
}
```

#### 4、通过纯代码创建和网络管理（示例）

```java
@Configuration
public class DemoConfig {
    @Bean
    public DynamicDataSource dsInit(){
        DynamicDataSource ds = new DynamicDataSource();
        ds.setDefaultTargetDataSource(...); //设置默认的源 //也可能没有默认
        return ds;
    }
}

@Controller
public class DemoController{
    @Inject
    DynamicDataSource ds;
  
    //动态添加
    @Mapping("ds/add")
    public void dsAdd(String dsName, String dsProps){
        Props props = new Props();
        props.loadAdd(Utils.buildProperties(dsProps));
        DataSource ds = props.getBean(HikariDataSource.class);
        
        ds.addTargetDataSource(dsName, ds);
    }
    
    //注解设置二级源
    @DynamicDs("${dsName}") //注解设置当前取用哪源
    @Mapping("ds/use")
    public void dsUse(String dsName){
        ...
        
        //如果想直接拿到数据源对象：ds.getTargetDataSource(dsName);
        //除了注解设置二级源，还可以手动设置：DynamicDsKey.setCurrent(dsName); 
    }
}
```

#### 具体参考

[https://gitee.com/noear/solon-examples/tree/main/4.Solon-Data/demo4002-dynamicds](https://gitee.com/noear/solon-examples/tree/main/4.Solon-Data/demo4002-dynamicds)
