JFinal使用技巧-多数据源自动切换和动态管理
JFinal使用技巧-多数据源自动切换和动态管理
自动切换:
Model的切换:
3.0 中 Model.getConfig() 的可见性由 private 改为 protected,
所以在你的 BaseModel 中重写这一个,不动其他代码 就搞定了Model自动切换数据源,
- @Override
- protected Config getConfig() {
- String configName = WebsiteInterceptor.getConfigName();
- if(configName == null)
- configName = DbKit.MAIN_CONFIG_NAME;
- return DbKit.getConfig(configName);
- }
Db的切换:
我用Ctrl + H 进行全局Db.替换为: Db.use(getConfigName()). 或者db().
PS:先搜索"Db.use(" 看系统中有这么用的没有,如果有先处理掉
我的各个Base层都有getConfigName()这个方法,
当然都是调的 WebsiteInterceptor.getConfigName();
各个BaseXxx层:
- /***
- * @Title: 获取访问者的 ConfigName
- * @return String 本次访问的ConfigName
- */
- public String getConfigName(){
- return WebsiteInterceptor.getConfigName();
- }
- public DbPro db(){
- return Db.use(getConfigName());
- }
拦截器WebsiteInterceptor:
- import javax.servlet.http.HttpServletRequest;
- import com.jfinal.aop.Interceptor;
- import com.jfinal.aop.Invocation;
- import com.jfinal.core.Controller;
- import com.jfinal.kit.PropKit;
- import com.jfinal.log.Log;
- import com.momathink.common.constants.DictKeys;
- import com.momathink.common.service.ActiveRecordPluginService;
- /**
- * @ClassName: 访问者的ConfigName管理
- * @author dufuzhong@126.com
- * @date 2016年10月3日 下午2:33:33
- */
- public class WebsiteInterceptor implements Interceptor {
- private static final Log log = Log.getLog(WebsiteInterceptor.class);
- private static final ThreadLocal<String> ME_CONFIGNAME = new ThreadLocal<String>();
- public static final String SERVER_NAME = "serverName";
- public static final String CONFIG_NAME = "configName";
- /***
- * @Title: 获取访问者的 ConfigName
- * @return String 本次访问的ConfigName
- */
- public static String getConfigName(){
- return ME_CONFIGNAME.get();
- }
- /* nginx 配置 内容:
- location xxxx {
- proxy_set_header Host $host:80;
- proxy_set_header X-Forwarded-For $remote_addr;
- proxy_redirect off;
- proxy_pass http://127.0.0.1:8080;
- # 配置多台tomcat 就 改 端口号等
- }
- */
- @Override
- public void intercept(Invocation inv) {
- Controller controller = inv.getController();
- HttpServletRequest request = controller.getRequest();
- String ipFromNginx = controller.getHeader("X-Forwarded-For");
- String serverName = request.getServerName();
- /*
- 如果你的数据源是已知固定的 在启动JFinal的时候 用 serverName 做数据源的 configName 也就是说他们是相等的,
- 到这里就可以完结了, 如果是动态的,需要看后的动态管理方式
- * */
- // String configName = serverName; 下面的判断也 改成 if(DbKit.getConfig(configName) != null){
- String configName = ActiveRecordPluginService.me.getConfigName(serverName);
- controller.setAttr(SERVER_NAME, serverName);
- controller.setAttr(CONFIG_NAME, configName);
- log.debug("访问者: 域名=" + serverName + " 资源K=" + configName + " IP=" + ipFromNginx);
- if(configName != null){
- ME_CONFIGNAME.set(configName);
- //异常必须在里面的拦截器进行捕捉处理
- inv.invoke();
- ME_CONFIGNAME.remove();
- } else {
- controller.renderError(403);
- }
- }
- }
如果你的数据源是已知固定的 在启动JFinal的时候
用 serverName 做数据源的 configName 也就是说他们是相等的,
到这里就可以完结了, 如果是动态的,需要看后的动态管理方式
动态管理:
ActiveRecordPluginService管理控制
- import java.net.URLEncoder;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import com.alibaba.fastjson.JSONArray;
- import com.alibaba.fastjson.JSONObject;
- import com.jfinal.kit.HttpKit;
- import com.jfinal.kit.JsonKit;
- import com.jfinal.kit.PropKit;
- import com.jfinal.kit.Ret;
- import com.jfinal.log.Log;
- import com.jfinal.plugin.IPlugin;
- import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
- import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
- import com.jfinal.plugin.activerecord.DbKit;
- import com.jfinal.plugin.activerecord.cache.EhCache;
- import com.jfinal.plugin.druid.DruidPlugin;
- import com.momathink.api.ApiInterceptor;
- import com.momathink.common.constants.DictKeys;
- import com.momathink.common.model.Site;
- /**(域名白名单)
- * <br>
- * 多站点配置信息和多数据源 管理
- * @author dufuzhong@126.com
- * @date 2016年11月8日 下午19:16:23
- */
- public class ActiveRecordPluginService implements IPlugin {
- private static final Log log = Log.getLog(ActiveRecordPluginService.class);
- public static final ActiveRecordPluginService me = new ActiveRecordPluginService();
- /***
- * 存储 多站点信息, 域名为 键, 数据源configName 为 值
- */
- private static final Map<String, String> CONFIGNAME_S = new HashMap<String, String>();
- static{//初始化值
- CONFIGNAME_S.put("localhost", DbKit.MAIN_CONFIG_NAME);
- CONFIGNAME_S.put("127.0.0.1", DbKit.MAIN_CONFIG_NAME);
- }
- private ActiveRecordPluginService(){}
- public static ActiveRecordPluginService me() {
- return me;
- }
- /***
- * @Title: 获取某站点 数据源configName
- * @param key 访问者域名
- * @return String 该站点数据源configName
- */
- public String getConfigName(String key) {
- return CONFIGNAME_S.get(key);
- }
- /***
- * 加载 多站点配置信息
- * @return
- */
- public synchronized boolean load(List<Site> sites){
- log.info("加载 多站点配置信息");
- for (Site site : sites)
- add(site);
- //报告记录一下启动情况
- String url = PropKit.get(DictKeys.SITE_SETCONFIGSITE).trim() + ApiInterceptor.getMaskKit();
- try {
- String data = URLEncoder.encode(JsonKit.toJson(sites), "UTF-8");
- log.info("报告多站点多数据源配置信息网站地址: "+ url);
- String post = HttpKit.post(url, "&data=" + data);
- log.info("报告完毕:" + post);
- } catch (Exception e) {
- log.info("报告异常,可能是没有启动运维服务器, 异常信息:" + e.getMessage());
- return false;
- }
- return true;
- }
- /***
- * 动态 配置 数据库参数 和 加载系统资源
- */
- public synchronized void add(Site site) {
- log.info("解析内容:" + site);
- //加 try catch 的原因是 不能因为某个 站点 的错误 配置信息, 影响到 其他的站点
- try {
- //配置 数据库参数 和 加载系统资源
- Ret addDb = init(site);
- //数据库启动载入, 启动成功则连接成功, 否则会异常
- boolean druidPlugin = ((DruidPlugin) addDb.get("druidPlugin")).start();
- boolean arp = ((ActiveRecordPlugin) addDb.get("arp")).start();
- //连接池和管理器都要成功true 才能通过
- if(!(druidPlugin) || !(arp)) throw new Exception("连接池和管理器");
- //网址域名
- String website = site.getWebsite().trim();
- //该网站的系统资源 KEY 值
- String configname = site.getConfigName().trim();
- //存储 该站点资源KEY信息
- CONFIGNAME_S.put(website, configname);
- //成功返回码
- site.keep("id");
- site.set(ApiInterceptor.ERRJSON_ERRCODE, 0).set(ApiInterceptor.ERRJSON_ERRMSG, "OK");
- } catch (Exception e) {
- //错误码表 后面再写... 先直接 看错误信息吧
- site.set(ApiInterceptor.ERRJSON_ERRCODE, 500).set(ApiInterceptor.ERRJSON_ERRMSG, e.getMessage());
- log.info("配置 数据库参数 错误信息:" + site);
- }
- }
- /***
- * 移除 动态 配置 数据库参数 和 加载的系统资源
- */
- public synchronized void del(Site site) {
- CONFIGNAME_S.remove(site.getWebsite());
- DbKit.removeConfig(site.getConfigName());
- //成功返回码
- site.keep("id");
- site.set(ApiInterceptor.ERRJSON_ERRCODE, 0).set(ApiInterceptor.ERRJSON_ERRMSG, "OK");
- }
- /**配置数据库参数 和 加载系统资源
- */
- private Ret init(Site site) {
- //该网站的系统资源 KEY 值
- String configname = site.getConfigName().trim();
- String jdbcurl = site.getJdbcUrl().trim();
- String user = site.getUser().trim();
- String password = site.getPassword().trim();
- // 配置DruidPlugin数据库连接池插件
- DruidPlugin druidPlugin = new DruidPlugin(jdbcurl, user, password);
- // 配置ActiveRecord插件
- ActiveRecordPlugin arp = new ActiveRecordPlugin(configname, druidPlugin);
- //false 是大写, true是小写, 不写是区分大小写, 看老项目情况配置
- arp.setContainerFactory(new CaseInsensitiveContainerFactory(false));
- //配置缓存类型
- arp.setCache(new EhCache());
- return Ret.create("druidPlugin", druidPlugin).set("arp", arp);
- }
- @Override
- public boolean start() {
- //TODO 我的测试代码放在 F:\workspace\moma_oa_test
- String url = PropKit.get(DictKeys.SITE_GETCONFIGSITE).trim() + ApiInterceptor.getMaskKit();
- log.info("获取多站点配置信息网站地址: "+ url);
- String jsonStr = HttpKit.get(url);
- log.debug("获取的多站点配置: "+ jsonStr);
- JSONObject json = JSONObject.parseObject(jsonStr);
- if(json.getInteger(ApiInterceptor.ERRJSON_ERRCODE) != 0){
- log.info("错误信息:" + json.getString(ApiInterceptor.ERRJSON_ERRMSG));
- return false;
- }
- //解析json
- List<Site> sites = JSONArray.parseArray(json.getString(ApiInterceptor.ERRJSON_DATA), Site.class);
- load(sites);
- return true;
- }
- @Override
- public boolean stop() {
- //TODO 报告这些站点停了
- return true;
- }
- }
想动态那就要接口调用了:
ConfigController:
- import com.alibaba.fastjson.JSONObject;
- import com.jfinal.aop.Before;
- import com.jfinal.aop.Clear;
- import com.momathink.common.annotation.controller.Controller;
- import com.momathink.common.base.BaseController;
- import com.momathink.common.model.Site;
- import com.momathink.common.service.ActiveRecordPluginService;
- /***多站点 系统配置 操作API
- * @author dufuzhong@126.com
- * @date 2017年2月25日 下午1:59:07
- */
- @Before({ApiInterceptor.class })
- @Controller(controllerKey = { "/api/config" })
- public class ConfigController extends BaseController {
- /** 动态管理数据库和系统资源接口
- */
- public void operation(){
- String data = getPara("data");
- Site site = JSONObject.parseObject(data, Site.class);
- if(site.isStateOn())
- ActiveRecordPluginService.me.add(site);
- else if(site.isStateOff())
- ActiveRecordPluginService.me.del(site);
- renderJson(ApiInterceptor.errJson(site));
- }
- }
接口 交接数据的 格式 规范:
- import com.jfinal.aop.Interceptor;
- import com.jfinal.aop.Invocation;
- import com.jfinal.core.Controller;
- import com.jfinal.kit.HashKit;
- import com.jfinal.kit.JsonKit;
- import com.jfinal.kit.StrKit;
- /**
- * 接口 交接数据的 格式 规范
- * 此拦截器仅做为示例展示,在本 demo 中 临时做一下 校验
- */
- public class ApiInterceptor implements Interceptor {
- /**接口 返回值 错误码 K */
- public static final String ERRJSON_ERRCODE = "errcode";
- /**接口 返回值 错误信息 K */
- public static final String ERRJSON_ERRMSG = "errmsg";
- /**接口 返回值 数据 K */
- public static final String ERRJSON_DATA = "data";
- /**接口 调用数据时 使用的 K */
- public static final String CHECK_MASK = "mask";
- public void intercept(Invocation inv) {
- Controller controller = inv.getController();
- String mask = controller.getPara(CHECK_MASK);
- if(isMask(mask)){
- try {
- inv.invoke();
- } catch (Exception e) {
- inv.getController().renderJson(errJson(1, e.getMessage()));
- }
- }else inv.getController().renderJson(errJson(401, "mask验证失败"));
- }
- /**判断 密钥*/
- private static boolean isMask(String mask){
- if(StrKit.notBlank(mask))
- return getMask().equals(mask.trim());
- return false;
- }
- /***获取通行码
- * 临时做一下 校验, 自行改造
- */
- public static String getMask(){
- String mask = (new Date().getTime()+"").substring(0, 8) + "_dufuzhong@126.com";
- //1.66665 分(min)
- return HashKit.md5(mask);
- }
- /***获取通行码
- */
- public static String getMaskKit(){
- return "&"+ CHECK_MASK +"=" + getMask();
- }
- //--------------
- public static String errJson(Integer errcode, String errmsg) {
- return "{\"errcode\":" + errcode + ",\"errmsg\":\"" + errmsg + "\"}";
- }
- public static String errJson(Object data) {
- return "{\"errcode\":0,\"errmsg\":\"OK\",\"data\":" + (JsonKit.toJson(data)) + "}";
- }
- public static String errJson() {
- return "{\"errcode\":0,\"errmsg\":\"OK\"}";
- }
- }
相关便利类;
- Site
- /**
- * Generated by JFinal.
- */
- @SuppressWarnings("serial")
- public class Site extends BaseSite<Site> {
- public static final Site dao = new Site();
- /**关机**/
- public static final Integer STATE_off = 0;
- /**开机**/
- public static final Integer STATE_on = 1;
- /**操作中**/
- public static final Integer STATE_out = 2;
- public boolean isStateOn(){
- return Site.STATE_on.equals(getState());
- }
- public boolean isStateOff(){
- return Site.STATE_off.equals(getState());
- }
- }
- /**
- * Generated by JFinal, do not modify this file.
- */
- @SuppressWarnings("serial")
- public abstract class BaseSite<M extends BaseSite<M>> extends Model<M> implements IBean {
- public void setId(java.lang.Integer id) {
- set("id", id);
- }
- public java.lang.Integer getId() {
- return get("id");
- }
- public void setConfigName(java.lang.String configName) {
- set("configName", configName);
- }
- public java.lang.String getConfigName() {
- return get("configName");
- }
- public void setWebsite(java.lang.String website) {
- set("website", website);
- }
- public java.lang.String getWebsite() {
- return get("website");
- }
- public void setJdbcUrl(java.lang.String jdbcUrl) {
- set("jdbcUrl", jdbcUrl);
- }
- public java.lang.String getJdbcUrl() {
- return get("jdbcUrl");
- }
- public void setUser(java.lang.String user) {
- set("user", user);
- }
- public java.lang.String getUser() {
- return get("user");
- }
- public void setPassword(java.lang.String password) {
- set("password", password);
- }
- public java.lang.String getPassword() {
- return get("password");
- }
- public void setHostId(java.lang.Integer hostId) {
- set("hostId", hostId);
- }
- public java.lang.Integer getHostId() {
- return get("hostId");
- }
- public void setState(java.lang.Integer state) {
- set("state", state);
- }
- public java.lang.Integer getState() {
- return get("state");
- }
- public void setErrcode(java.lang.Integer errcode) {
- set("errcode", errcode);
- }
- public java.lang.Integer getErrcode() {
- return get("errcode");
- }
- public void setErrmsg(java.lang.String errmsg) {
- set("errmsg", errmsg);
- }
- public java.lang.String getErrmsg() {
- return get("errmsg");
- }
- }
到此基本完结了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!