jfinal 动态多数据源实现

jfinal目前提供了多数据源的支持,但是根据前端传过来的业务标识去动态的切换数据源的场景还显得有些力不从心,我看到社区也有一部分关于这方面的分享,但是总感觉不够简洁,要改的东西比较多,下面分享一下我的实现,希望对大家有所启发。

原理: 因为对数据库的操作,最后都用到了DataSource.getConnection方法,所以这个方案的原理就是重写DataSource的getConnection方法,在调用到getConnection的时候根据业务标识去初始化DataSource

1. 定义一个多数据源DataSource

public class RoutingDatasource implements DataSource {
	//缓存多个datasource
	private Map<String, DataSource> datasources = new HashMap<String,DataSource>();
	
	//重写 getConnection 方法
	//businessFlag 就是业务标识信息,表示要连哪个数据库,可以放在ThreadLocal里面,每个线程不一样
	@Override
	public Connection getConnection() throws SQLException {
		DataSource routingDataSource = datasources.get("businessFlag");
		if(routingDataSource == null) {
			synchronized (this) {
				routingDataSource = datasources.get("businessFlag");
				if(routingDataSource == null) {
					routingDataSource = createDatasource("businessFlag");
					datasources.put("businessFlag", routingDataSource);
				}
			}
		}
		return routingDataSource.getConnection();
	}
	
	//根据不同的业务标识创建DataSource
	//可以把不同数据库的连接信息放到数据库中,然后这个地方读取连接信息
	private  DataSource createDatasource(String businessFlag) {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setUrl("");// url
		dataSource.setUsername(""); // username
		dataSource.setPassword(""); // password
		return dataSource;
	}
}

2.创建ActiveRecordPlugin 的时候调用下面的构造函数

DruidPlugin dbPlugin = new DruidPlugin(new RoutingDatasource());


这样每次前端发来请求之后,在进行数据库操作之前都会初始化相应的DataSource,然后获取connection,从而实现了动态数据源的切换

3.效果

请求1   http://localhost:8080/api/test?businessflag=test1

image.png

请求2  http://localhost:8080/api/test?businessflag=test2

image.png

4. 注意

此方案适用于不同数据源,但是要求数据结构一致,否则在使用model的时候可能会报找不到表的异常

5. demo地址

https://gitee.com/git_zhanglong/RoutingDatasource

欢迎大家批评指正


关注我的公众号,免费获取Java + Jfinal学习视频

image.png


评论区

山东小木

2019-07-26 14:07

我在JFinal1.2的时候做了用配置文件配置 然后saas平台给不同地域的用户开户数据分库分表到不同的区域数据库服务器上 是在开户信息的时候 自动选择绑定了 然后 所有查询只要按照用户自己绑定的数据源信息操作了

快乐的蹦豆子

2019-07-26 14:15

@山东小木 里面有多少个ActiveRecordPlugin

山东小木

2019-07-26 14:47

@快乐的蹦豆子 看你配置了几个数据源

快乐的蹦豆子

2019-07-26 15:00

@山东小木 我写这个的目的主要是解决一个ActiveRecordPlugin 提供多个数据源的问题,当然如果数据库连接从表中取的话,应该有两个 ActiveRecordPlugin

AI-wen

2019-07-29 10:31

试了下 不太好使啊 能写个再详细的?

快乐的蹦豆子

2019-07-30 09:58

@AI-wen 抽空放到git上

AI-wen

2019-07-30 10:58

小王大哥

2019-08-03 08:31

期待分享

快乐的蹦豆子

2019-08-03 12:24

比较适用于代码集中部署一套,但是数据库分开的应用

scocai

2019-08-05 09:12

主数据源保持原来的,异构系统数据源才通过数据库来管理,能实现吗?

快乐的蹦豆子

2019-08-06 10:37

@scocai 不明白啥意思,详细的描述下需求

scocai

2019-08-07 15:18

@快乐的蹦豆子 系统的主数据源就一个,不需要数据库来管理,我们在配置文件里配置就行了,其他的数据源,比如:同步第三方系统数据,或者和第三方系统进行数据交换等,这些第三方数据源我希望是通过数据库来管理,以方便在业务代码里进行调用。目前jfinal里切换数据源是 Db.use("数据源名称")来处理的。数据源名称是配置文件配置好数据连接,启动时候用 new ActiveRecordPlugin 方式加载。没法放到数据库里。

快乐的蹦豆子

2019-08-07 20:56

@scocai 我这个稍微一改就满足你的要求了
1 定义一个主数据源的activerecord
2 在routingdatasource里面选择数据源的代码,用主数据源根据业务标示去去

scocai

2019-08-21 10:55

@快乐的蹦豆子 怎么改呢?可以做一个完整的demo看看吗?

dogu

2020-02-26 19:41

以Mysql为例:

String url = "jdbc:mysql://localhost:3306/dogu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8";
String username = "root";
String password = "root";
String drive = "com.mysql.cj.jdbc.Driver";
Map map = new HashMap<>();
map.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME, drive);
map.put(DruidDataSourceFactory.PROP_URL, url);
map.put(DruidDataSourceFactory.PROP_USERNAME, username);
map.put(DruidDataSourceFactory.PROP_PASSWORD, password);

Dialect dialect = new MysqlDialect();
boolean showSql = true;
boolean devMode = false;
int transactionLevel = 0;
ICache cache = new EhCache();
Config config = new Config("NEW_DB_KEY", DruidDataSourceFactory.createDataSource(map), dialect, showSql, devMode, transactionLevel, IContainerFactory.defaultContainerFactory, cache);
DbKit.addConfig(config);

dogu

2020-02-26 19:43

@dogu 获取的时候使用:DbKit.getConfig("NEW_DB_KEY");或者是Db.use("NEW_DB_KEY");即可

快乐的蹦豆子

2020-02-27 13:56

侧重于动态创建,你这都写死了@dogu

dogu

2020-03-20 17:24

这个就可以动态的在controller中创建,创建完之后在其他地方用就可以了。支持同种数据库和不同种数据库同连接

马小酱

2020-07-02 22:27

您这个确实是一个新的思路,很不错!之前没想到

快乐的蹦豆子

2020-07-03 10:32

@马小酱 其实我们生产中就用的这种方案,不过是springboot 框架上。

一路走来

2021-01-26 18:55

你这个最大的好处就是未改变业务代码,如果可行的话,我觉得是比较好的解决方案

快乐的蹦豆子

2021-01-27 15:05

@一路走来 我们在生产环境用了多年的解决方案,虽然不是jfinal,用的springboot,但是道理是一样的

一路走来

2021-02-27 15:40

@快乐的蹦豆子 感谢分享,推荐使用

一路走来

2021-02-28 16:15

@快乐的蹦豆子 非常感谢,测试通过,同时把多租户对应同一个数据库也搞定了,感谢分享,一般程序员还真搞不定。感谢感谢感谢!!!

快乐的蹦豆子

2021-10-25 10:32

@一路走来 感谢支持和实践

热门分享

扫码入社