NutzCN Logo
问答 nutzboot 能不能配置多数据源
发布于 2171天前 作者 文涛(wentao) 2285 次浏览 复制 上一个帖子 下一个帖子
标签:

项目需要同事访问多个库,如何配置多数据源,并支持多数据源的读写分离

30 回复

希望可以直接通过配置后在代码中可以直接注入

比如:
@Inject
protect Dao dao;
@Inject(name="daoA")
protect Dao daoA;

有两个办法: 加个src/main/resources/ioc/dao2.js 或者 写个MyBeans

package io.nutz.demo.simple;

import javax.sql.DataSource;

import org.nutz.dao.Dao;
import org.nutz.dao.impl.NutDao;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;

import com.alibaba.druid.pool.DruidDataSource;

@IocBean
public class MyBeans {

    @Inject
    protected PropertiesProxy conf;
    
    @IocBean
    public Dao dao2(@Inject DataSource dataSource2) {
        return new NutDao(dataSource2);
    }
    

    @IocBean
    public DataSource dataSource2(@Inject DataSource dataSource2) {
        return conf.make(DruidDataSource.class, "jdbc2.");
    }
}

使用 MyBeans 的方式貌似进了一个死循环,不听的 Get dataSource2

把 dataSource2方法里的@Inject DataSource dataSource2参数去掉就可以了

我的锅。。。

@wendal 今天在继续使用这个方法引入其它数据源,发现貌似没有识别主从
我跟了DataSourceStarter类的getSlaveDataSource方法,发现传进来的prefix始终为:jdbc.slave,所以匹配不上slave
我自己定义如下:

@IocBean
public class MyBeans {

    @Inject
    protected PropertiesProxy conf;
    
    @IocBean
    public Dao daoPlay(@Inject DataSource dataSourcePlay) {
        return new NutDao(dataSourcePlay);
    }
    

    @IocBean
    public DataSource dataSourcePlay() {
        return conf.make(DruidDataSource.class, "jdbc.play.");
    }
}

配置文件为:

jdbc.play.type=druid
jdbc.play.url=jdbc:mysql://192.168.0.11:13306/
jdbc.play.username=play
jdbc.play.password=oj%w9DB$cu&$5yv^
jdbc.play.validationQuery=select 1;
jdbc.play.slave.A.url=jdbc:mysql://192.168.0.12:13306/
jdbc.play.slave.A.username=play
jdbc.play.slave.A.password=oj%w9DB$cu&$5yv^
jdbc.slave.A.validationQuery=select 1;

@qq_b1ca03f7 来个pr可以不? ^_^

我猜到了这个结局,等下我试试,😓

看了下nutzboot的NutDaoStarter源码,其中自动注入了jdbc.slave的从库

        // 看看是不是需要注入从数据库
        if (Lang.loadClassQuite("org.nutz.boot.starter.jdbc.DataSourceStarter") != null) {
            DataSource slaveDataSource = DataSourceStarter.getSlaveDataSource(ioc, conf, "jdbc.slave.");
            if (slaveDataSource != null) {
                NutDaoRunner runner = new NutDaoRunner();
                runner.setSlaveDataSource(slaveDataSource);
                dao.setRunner(runner);
            }
        }

感觉这个地方是不需要改,也不能改,因为无法拿到另外注入的dao对象和prefix,所以感觉要从nutz的dao对象入手改造,我在想是不是可以给dao对象重载一个方法来进行从库的加载

                NutDaoRunner runner = new NutDaoRunner();
                runner.setSlaveDataSource(slaveDataSource);
                dao.setRunner(runner);

不需要重载吧, 这段代码不就是设置从数据库了?

是的,我刚刚又再深入看了下 DataSourceStarter.getSlaveDataSource 方法实现了多从库的灵活调度,所以看来还是得在nb上想办法扩展,但暂时想不到怎么扩展,还是基础不够扎实,尴尬

我现在已经将设置部分的代码,放到我的dataSourcePlay方法里,也可以用了,只是还想知道nb要怎么扩展下,让这些代码可以服用,让多数据库注入更加简单,不仅仅是复制粘贴来实现

我现在能想到的是把NutDaoStarter另外封装一个类出来,然后构造函数留一个prefix参数,类继承自NutDao,然后这样注入:

@Inject
public NutDao daoPlay() {
    return new NutDaoCustom("jdbc.play.");
}
@Inject
public NutDao daoNews() {
    return new NutDaoCustom("jdbc.news.");
}

我建议呢,数据源最好能托管给ioc

只是建议,大家讨论讨论一下

我的理解是ioc.get方法调用一次对象生成是不是就被ioc托管了?

算是的,

不过nutzboot的进程通常都是直接关, 所以问题不大

DataSourceStarter.getSlaveDataSource事实上也没放进ioc里面去,啊啊啊...

如果是这样,我觉得可以形成一个配置规范;
比如:jdbc.{dbname}.url 主库
jdbc.{dbname}.slave.A.url 从库
jdbc.{dbname}.slave.B.url 从库
然后NutDaoStarter在加载时,自动扫描conf内所有{dbname},然后根据{dbname}注入所有db,且注入名直接就是{dbname}Dao
假设{dbname}有:play、news等, 那么使用时就直接:

@Inject
public Dao playDao;
@Inject
public Dao newsDao;

不知道是否可行

这个思路可以的

那我就跃跃欲试了,哈哈

顺带可以把我的 https://nutz.cn/yvr/t/u8boo9deokhnkqig232cmkfk7i 写个新的RedisStarter,再结合 DynaDataSourceSeletor 实现一个选择器,就可以完成多组redis主从配置了

@wendal 请教下,下面这个拦截器我可以统一设置到每一个不通库的dao上吗?会不会有冲突

        NutDao dao = new NutDao(dataSource, sqlManager);
        List<Object> interceptors = new ArrayList<>();
        // 是否启用了DaoCache呢?
        if (conf.getBoolean(PROP_INTERCEPTOR_CACHE_ENABLE, false)) {
            interceptors.add(ioc.get(DaoCacheInterceptor.class));
        }
        // 日志拦截器
        if (conf.getBoolean(PROP_INTERCEPTOR_LOG_ENABLE, true)) {
            interceptors.add("log");
        }
        // sql耗时拦截器
        if (conf.getBoolean(PROP_INTERCEPTOR_TIME_ENABLE, true)) {
            interceptors.add("time");
        }

额, 加个前缀什么吧?? 没看懂

@wendal 我先简单实现了一版,已经PR了,你帮忙看看,给点建议

好,得明天看了

已更新,不过我的这部分代码现在只支持druid

添加回复
请先登陆
回到顶部