如何动态切换数据源

波总好!大家好!

    小弟有个问题想请教大家,如何在登录的时候根据用户登录时从表中的一个字段来确定该用户是属于哪一个数据库中的用,所以需要使用到数据源动态切换,因为刚开始用Jfinal,所以遇到了很多问题,所以向大家请教。

评论区

JFinal

2017-08-04 11:59

通常用 Db.use(configName) 去动态切换数据源

Model 所对应的数据源是确定的不需要动态切换,不过 Model 的数据源仍然是可以通过 model.use(configName) 临时进行切换的,也就是说为当前这个 model 对象切换数据源

dear7575

2017-08-04 12:02

@JFinal 好的,感谢您的回复,我先去试试看能不能解决!哪里有关于Db.use()这个方法的详细介绍吗?

JFinal

2017-08-04 13:27

@dear7575 jfinal 手册上有说明,还有例子代码,在本站首页可以下载到

dear7575

2017-08-04 14:10

@JFinal 好的,谢谢

dear7575

2017-08-04 14:27

@JFinal 我刚刚看了一下多数据源支持,这个不是我想要,因为这个数据源切换是自己指定的数据库,而我需要的切换数据库是前端需要创建一个商户,在创建商户的同时会为这个商户创建一套数据库,数据库名称这些东西都是不指定的,会根据这个商户的某些东西来区分,然后在该商户登录时,根据我们指定的值,让这个商户连接自己的数据库。同时根据这个值来切换数据库。这个怎么样才能做到?

JFinal

2017-08-04 14:39

@dear7575 使用一个全局拦截器,关注一下 DbKit.getConfig() 出来的 Config 对象中有一个 setThreadLocalConnection(Connection ) 方法

用这个方法可以直接为当前线程提供一个 connection,那么后续的数据库操作都会直拦支使用这个 connnection 对象了

更进一步,参考一下 com.jfinal.plugin.activerecord.tx.Tx.java 这个源代码,里面有这种用法,大致分几步:
1:在拦截器中通过某种方式得知当前操作的用户身份,根据登录信息很容易知道
2:通过用户身份,得到该用户的 configName
3:通过 DbKit.getConfig(configName) 得到这个用户的 config 对象
4:通过 config.setThreadLocalConnection(config.getDataSource().getConnection()) 为当前线程设置上 connection 对象
5:调用 inv.invoke()
6:在 finally 块中调用 config.close(connection)

JFinal

2017-08-04 15:43

这里再补充一点,对于 model 来说,要切换数据源,可以在拦截器里面弄一个 ThreadLocal 保存 config 对象

然后引入一个 MyBaseModel extends Model, 让你所有最终的 model 继承这个 MyBaseModel ,里头覆盖掉 _getConfig() 方法,利用 ThreadLocal 切到你想使用的 config 上去

dear7575

2017-08-04 15:45

@JFinal 从刚刚从Tx.java方法中复制过来后,自己写了一个拦截器,然后在public void configInterceptor(Interceptors me) 进行配置我刚刚写的拦截器,在public void intercept(ActionInvocation invocation) 中我输出了DbKit.getConnection() 这个时候看到输出来的是URL= jdbc:mysql://......路径。这样对吗?

JFinal

2017-08-04 15:52

@dear7575 ActionInvocation 是 jfinal 很老版本中的类名了,后来的版本早就是是 Invocation,建议升级到新版本

此外,参考我前面的说明尝试,拦截器的用法在手册中都有详细的说明,多多调试代码,实现你想要的功能是一定可以的

dear7575

2017-08-04 15:58

@JFinal 谢谢,我现在就去测试。

JFinal

2017-08-04 16:03

@dear7575 注意最新版本的 jfinal 的 Model 中有一个 protected Config _getConfig() 方法,这个方法是可以通过继承并覆盖,从而实现数据源切换的

简单来说,要在整上切换 model 的数据源,并且对开发者完全透明化,只需要做两点:
1:用一个全局拦截器 + ThreadLocal 将当前线程的 Config 绑定到当前线程
2:通过引入一个中间 MyModel extends Model 覆盖 _getConfig() 方法,并在这个方法中从全局拦截器中获取 ThreadLocal 中准备好的 Config 对象

而 Db.xxx(...) 系列方法的数据源自动化切换也大体可以用上面的方式,只需要将第 2 步改为以下的方式:
让 Db.xxx(...) 方法使用的 config 对象也参考上述方式进行切换

杜福忠

2017-08-08 12:36

和我的一个需求是一模一样的啊...
http://www.jfinal.com/share/236

dear7575

2017-08-09 09:50

@杜福忠 对这框架还不是很熟悉,所以很多东西都不知道怎么写。尴尬啊!

dear7575

2017-08-15 11:22

@杜福忠 String url = PropKit.get(DictKeys.SITE_GETCONFIGSITE).trim() + ApiInterceptor.getMaskKit();
DictKeys是什么呢?或者发给我我自己研究一下下!求教,谢谢啦!

杜福忠

2017-08-15 14:17

@dear7575 这个是配置文件的KEY , 配置的是一个HTTP的接口地址 , 我这边的需求是远程获取数据库连接信息, JSON格式

dear7575

2017-08-15 14:26

@杜福忠 那我如果不需要远程获取数据库连接信息,该如何改写?谢谢。

杜福忠

2017-08-16 09:21

@dear7575 不需要的话, 直接使用最前面的那个方法, 我在代码中已经做注释了, 后面的那个管理类就不需要, 直接走业务

dear7575

2017-08-16 11:32

@杜福忠 弄不出来,心累!一点思路都没有。

serverName: 127.0.0.1
key: 127.0.0.1
访问者: 域名=127.0.0.1 资源K=main IP=null

dear7575

2017-08-16 15:50

@JFinal 波哥你好!我到现在还没解决这个动态切换数据库的问题,我也把杜福忠的发布的动态切换数据源看了很多遍,我也自己测试了,一点思路都没有,俱乐部里有切换数据数据的代码吗?如果有的话,我可以加入俱乐部!

杜福忠

2017-08-16 16:50

@dear7575
serverName=a.baidu.com
key=a
----------
serverName=b.baidu.com
key=b

如这种吗?
还是挂参的形式?

JFinal

2017-08-16 17:02

@dear7575 用一个独立的表去存放 userName 与 configName 的关系,查询的时候用 Db + Record 模式:
1:用一个全局拦截器
2:拦截器中得到当前登录用户的 userName,这个就是传统的登录功能,不再多说
3:String configName = Db.queryString("select configName from configTable where userName=?", userName);
4:将这个 configName 放在当前拦截器的一个 static ThreadLocal threadLocal 对象中
5:通过引入一个中间的 MyBaseModel extends Model,覆盖一下父类的 _getConfig() 方法,从 threadLocal 中拿到 configName,然后直接:
return DbKit.getConfig(configName);
6:最终的 Model 继承一下这个 MyBaseModel,如果用了生成器生成过 BaseModel,可以让所有生成的 BaseModel 由继承 Model 改为继承 MyBaseModel,扩展一下 BseModelGenerator 中的 genClassDefine() 方法即可,极度容易

dear7575

2017-08-17 11:23

@杜福忠 不是的,我的需求是动态切换数据库!
jdbc:mysql://localhost/jfinal_demo?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull

localhost地址是固定的,
jfinal_demo而这个数据库需要动态切换。如何满足这个需求?谢谢!

JFinal

2017-08-17 11:32

@dear7575 根据不同的 dataBaseName 按照我前面的方案,去 return 不同的 Config 对象就可以了

每个 Config 都可以对就到不同的 dataBaseName, Config 中有很多构造方法,中有一个一个是 DataSource 参数,这个 DataSource 参数可以对应到不同的 jdbcUrl,也就是说可以对应到不同的 database

杜福忠

2017-08-17 11:44

@dear7575 如果使用:
http://www.jfinal.com/share/236
这个的话: 是在拦截器WebsiteInterceptor:
ME_CONFIGNAME.set(configName);<一般规则是: URL挂参, 登录用户信息, 域名区分, 等 取configName

杜福忠

2017-08-17 11:45

@dear7575
动态管理:
ActiveRecordPluginService管理控制
这个就不用看了, 是业务关联的

热门反馈

扫码入社