下面是通过改造基本Model类,并动态配置数据源的方法:
1、新增数据源缓存类
注意,configName要使用线程本地类,JBoot非线程安全。
public class DataConfigName { private static DataConfigName me; @JSONField(serialize = false) transient private ThreadLocalconfigName = new ThreadLocal<>(); private DataConfigName(){} public static DataConfigName getInstance(){ if(me == null){ me = new DataConfigName(); } return me; } public void setConfigName(String name){ configName.set(name); } public String getConfigName(){ String name = configName.get(); if(null == name){ name = DbKit.MAIN_CONFIG_NAME; } return name; } }
2、修改Model基础类
JBoot操作数据库主要通过Model来实现,框架提供了基础类JBootModel,需要对这个类进行扩展,覆盖所有方法,如下所示,方法中都增加了use子句,实现数据源的切换,否则会使用默认数据源:
public class GlueModel> extends JbootModel { public GlueModel(){}; …… public M use(String configName) { return super.use(configName); } M superUse(String configName) { return super.use(configName); } public boolean saveOrUpdate() { this.use(DataConfigName.getInstance().getConfigName()); return super.saveOrUpdate(); } public boolean save() { this.use(DataConfigName.getInstance().getConfigName()); return super.save(); } …… }
3、替换所有Db类的操作,增加use子句,如:
public class TransactionModel extends GlueModel{ …… public static TransactionModel getByTno(String tno){ …… Record record = Db.use(DataConfigName.getInstance().getConfigName()).findFirst(sql); …… } …… }
4、创建一个数据源动态管理类。
Jboot虽然在2.1.2版本的DataSourceConfigManager类中开放了addConfig方法,但由于Db类是属于JFinal框架的,无法影响到,因此需要调用JFinal的DbKit的addConfig方法完成数据源的新增。
下面的代码只提供了从默认数据库获取数据源配置参数的实现方式,如果需要通过本地配置文件、网络文件等方式,可参考自行添加
public class DynamicDataSourceManager { /** * 从数据库获取数据源配置参数 * @param tableName * @param corpId * @return */ public static Config loadConfigFromDB(String tableName, String corpId){ Config config = DbKit.getConfig(corpId); if(null == config) { SqlPara sql = new SqlPara(); sql.setSql("select * from " + tableName + " where corpid=?").addPara(corpId); Listrecords = Db.find(sql); if (null != records && records.size()>0) { Record record = records.get(0); DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setName(record.getStr("corpid")); dataSourceConfig.setType(DataSourceConfig.TYPE_MYSQL); dataSourceConfig.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSourceConfig.setUrl("jdbc:mysql://" + record.getStr("write_host") + ":" + record.getStr("write_port") + "/" + record.getStr("write_database") + "?useUnicode=true&characterEncoding=" + record.getStr("charset") + "&useSSL=false"); dataSourceConfig.setUser(record.getStr("write_username")); dataSourceConfig.setPassword(record.getStr("write_password")); dataSourceConfig.setPoolName(record.getStr("write_database")); DataSource dataSource = new DataSourceBuilder(dataSourceConfig).build(); config = new Config( record.getStr("corpid"), dataSource, new MysqlDialect(), false, false, DbKit.DEFAULT_TRANSACTION_LEVEL, IContainerFactory.defaultContainerFactory, new EhCache() ); DbKit.addConfig(config); } } return config; } }
5、在切片方法或入口方法中实现数据源的配置
…… if(null != corpId){ Config config = DynamicDataSourceManager.loadConfigFromDB("corp_database", corpId); if(null != config) { DataConfigName.getInstance().setConfigName(corpId); } } ……
通过上述修改即可完成动态分库处理,实际的Model中无需再做任何特殊处理。