去年做的一个spring小项目,现在打算转为jfinal,看了下jfinal的sql模板和Template Engine,觉得可能会更适合我的这个项目。
在俱乐部群里看到 @杜福忠 有发过相关讨论,但还有些疑问,故发此帖,希望获得帮助,也许这样能帮助更多有次疑问的人。
项目情况:
本公司有两款分销软件,分布在不同服务器,本项目就是从不同服务器读取数据,然后呈现报表给相应的客户(一个客户对应一个数据库)。
目前项目建了一个maindb数据库,里面存放了客户各个数据库连接地址。本项目配置文件中配置了maindb的数据库地址。
客户登录时,先到maindb中获取相应的客户数据库连接地址,登录成功后,从相应的客户数据库中获取数据。
如下图:
本人JFinal动态连数据库主要Demo代码如下:
public class IndexController extends Controller { public void index() { String ztnum = "old"; //String ztnum = getPara("ztnum"); // 根据JFinal configPlugin中配置的主数据库, 连接到主数据库maindb,然后获取客户数据库连接信息 List<Record> recList = Db.find("select * from yzn_sys_zt where ztnum=? limit 0,1",ztnum); if (recList.size()>0) { Record rec = recList.get(0); DruidPlugin dp = null; ActiveRecordPlugin arp = null; // 获取configname,如果抛空指针异常,则配置数据库连接池及ActiveRecord插件,否则可以直接使用configname进行数据查询 try { DbKit.getConfig(ztnum).getName(); } catch (NullPointerException e) { // 根据参数拼接数据库连接地址 String dburl = "jdbc:sqlserver://"+rec.getStr("dbhost")+":"+rec.getStr("dbport")+";DatabaseName="+rec.getStr("dbname"); // 配置DruidPlugin数据库连接池 dp = new DruidPlugin(dburl, rec.getStr("dbusername"), rec.getStr("dbpassword")); // 配置ActiveRecord数据库连接池插件 arp = new ActiveRecordPlugin(rec.getStr("ztnum"),dp); dp.start(); arp.start(); } List<Record> recList2 = Db.use(ztnum).find("select top 2 * from sys_user"); for(Record r:recList2){ System.out.println("测试信息 查询客户数据库中数据信息:"+r.toJson()); } // 这里是否需要stop,测试时发现不stop,数据库中会保留很多连接,如果不stop还会有什么问题? // 而且网上说创建一个数据库连接池比较耗资源,这样的话是不是没必要 每次客户查询 就创建一次数据库连接池,这个利弊如何平衡? // arp.stop(); // dp.stop(); render("index.html"); } else { // 获取不到连接信息,跳转到错误页。 render("error.html"); } } }
疑惑:
1、如上代码写法是否有会有问题? 或者有没有更好的方案?
2、关于stop,测试时发现不stop,数据库中会保留数据库连接,如果不stop还会有什么问题?
而且网上说创建一个数据库连接池比较耗资源,这样的话是不是没必要 每次客户查询 就创建一次数据库连接池?
这个利弊如何平衡?
2:如果动态创建 ActiveRecordPlugin 与其依赖的 DuirdPlugin,那么都需要动态回收资源,需要调用 ActiveRecordPlugin 以及 DruidPlugin 的 stop() 方法
创建数据库连接池会有一定的延迟,所以通常是系统初始化的时候创建,下面给出新的方案:
1:在系统启动的时候,读出所有客户有关数据库的信息,一次性统一创建好 ActiveRecordPlugin
2:如果后期有新客户加入,那么在加入的同时就创建好 ActiveRecordPlugin
3:为了避免多个 ActiveRecordPlugin 在启动时就耗尽数据库允许的最大连接数,所以要需要控制 DruidPlugin 的初始连接个数
4:创建一个名为 ConfigNameInterceptor 的全局拦截器,里面放一个 ThreadLocal属性来存放当前请求客户的 configName,通过查询主数据库设定好合适的 configName
5:用户请求某个业务时,业务从 ConfigNameInterceptor 的 ThreadLocal 中获取 configName
6:代码中统一: Db.use(configName).find(...) ,这样就实现了业务层对所有客户都是完全一样代码的目标