千家信息网

​​​​​​​Spring多租户数据源管理AbstractRoutingDataSource怎么用

发表于:2025-02-24 作者:千家信息网编辑
千家信息网最后更新 2025年02月24日,本篇内容介绍了"Spring多租户数据源管理AbstractRoutingDataSource怎么用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处
千家信息网最后更新 2025年02月24日​​​​​​​Spring多租户数据源管理AbstractRoutingDataSource怎么用

本篇内容介绍了"Spring多租户数据源管理AbstractRoutingDataSource怎么用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1.基本原理

多数据源能进行动态切换的核心就是spring底层提供了AbstractRoutingDataSource类进行数据源路由。AbstractRoutingDataSource实现了DataSource接口,所以我们可以将其直接注入到DataSource的属性上。

我们主要继承这个类,实现里面的方法determineCurrentLookupKey(),而此方法只需要返回一个数据库的名称即可。

比如,Controller通过拿到前端业务传递的数值,进行业务逻辑分发。它就可以手动设置当前请求的数据库标识,然后路由到正确的库表里面。

@Controllerpublic class ARDTestController {    @GetMapping("test")    public void chifeng(){        //db-a 应该是上层传递下来的属性,我们可以把它放在ThreadLocal里        DataSourceContextHolder.setDbKey("db-a");    }}

那么当sql语句执行的时候,它如何知道自己需要切换到哪个数据源呢?是不是需要把db-a这个属性一直透传下去呢?

在Java中,可以使用ThreadLocal绑定这个透传的属性。像Spring的嵌套事务等实现的原理,也是基于ThreadLocal去运行的。所以,DataSourceContextHolder.本质上是一个操作ThreadLocal的类。

public class DataSourceContextHolder {    private static InheritableThreadLocal dbKey = new InheritableThreadLocal<>();    public static void setDbKey(String key){        dbKey.set(key);    }    public static String getDbKey(){        return dbKey.get();    }}

2.配置代码

首先,我们自定义了配置文件的格式。如下面的代码,就配置了db-a和db-b两个数据库。

multi:  dbs:    db-a:      driver-class-name: org.h3.Driver      url: jdbc:h3:mem:dba;MODE=MYSQL;DATABASE_TO_UPPER=false;    db-b:      driver-class-name: org.h3.Driver      url: jdbc:h3:mem:dbb;MODE=MYSQL;DATABASE_TO_UPPER=false;

然后,我们将它解析称properties。

@ConfigurationProperties(prefix = "multi")@Configurationpublic class DbsProperties {    private Map> dbs = new HashMap<>();    public Map> getDbs() {        return dbs;    }    public void setDbs(Map> dbs) {        this.dbs = dbs;    }}

接下来一步,需要配置整个应用所默认的数据源。如你所见,它的主要逻辑,就是在运行的时候,从ThreadLocal里取出提前设置的这个值。

public class DynamicDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        return DataSourceContextHolder.getDbKey();    }}

最后一步,设置整个项目中默认的DataSource。注意,我们生成DynamicDataSource之后,还需要提供targetDataSource和defaultTargetDataSource两个属性的值,才能够正常运行。

@Configurationpublic class DynamicDataSourceConfiguration {    @Autowired    DbsProperties properties;    @Bean    public DataSource dataSource(){        DynamicDataSource dataSource = new DynamicDataSource();        final Map targetDataSource  = getTargetDataSource();        dataSource.setTargetDataSources(targetDataSource);        //TODO 默认数据库需要设置        dataSource.setDefaultTargetDataSource(targetDataSource.values().iterator().next());        return dataSource;    }    private Map getTargetDataSource(){        Map dataSources = new HashMap<>();        this.properties.getDbs().entrySet().stream()                .forEach(e->{                    DriverManagerDataSource dmd = new DriverManagerDataSource();                    dmd.setUrl(e.getValue().get("url"));                    dmd.setDriverClassName(e.getValue().get("driver-class-name"));                    dataSources.put(e.getKey(),dmd);                });        return  dataSources;    }}

3.问题

通过以上简单的代码,就可以实现Spring简单的多数据源管理。但明显的,它还存在很多问题。

  • 需要产品设计选择模式,进行业务切换。

  • 前端可以采用放在localStroage的方式,保存属性,可使用拦截器方式将变量每次都传递。

  • 后端每次请求,都需要带上目标db,可以采用放在ThreadLocal里的方式。但ThreadLocal有线程透传的问题,如果任务里开启了子线程,则变量不能共享。

  • 由于表是动态选择的,所以JPA自动创建和update等模式,将不可用。不方便测试和单元测试,在测试接口的时候,也需要每次强制指定指向的库。

  • 由于是修改数据源的模式,每次增加库,都需要重新启动上线才可以。如果要做到动态性,数据源销毁是个问题。

"Spring多租户数据源管理AbstractRoutingDataSource怎么用"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0