• JFinal使用技巧-多数据源自动切换和动态管理


    JFinal使用技巧-多数据源自动切换和动态管理

    https://jfinal.com/share/236

    自动切换:

    Model的切换:

    3.0  中 Model.getConfig() 的可见性由 private 改为 protected,
    所以在你的 BaseModel 中重写这一个,不动其他代码 就搞定了Model自动切换数据源,

    1.     @Override
    2.     protected Config getConfig() {
    3.         
    4.         String configName = WebsiteInterceptor.getConfigName();
    5.         
    6.         if(configName == null)
    7.             configName = DbKit.MAIN_CONFIG_NAME;
    8.         
    9.         return DbKit.getConfig(configName);
    10.     }

    Db的切换:

    我用Ctrl + H 进行全局Db.替换为: Db.use(getConfigName()). 或者db().

    PS:先搜索"Db.use(" 看系统中有这么用的没有,如果有先处理掉

    我的各个Base层都有getConfigName()这个方法,
    当然都是调的 WebsiteInterceptor.getConfigName();

    各个BaseXxx层:

    1. /***
    2.      * @Title: 获取访问者的 ConfigName
    3.      * @return String    本次访问的ConfigName
    4.      */
    5.     public String getConfigName(){
    6.         return WebsiteInterceptor.getConfigName();
    7.     }
    8.     public DbPro db(){
    9.         return Db.use(getConfigName());
    10.     }

    拦截器WebsiteInterceptor:

    1. import javax.servlet.http.HttpServletRequest;
    2.  
    3. import com.jfinal.aop.Interceptor;
    4. import com.jfinal.aop.Invocation;
    5. import com.jfinal.core.Controller;
    6. import com.jfinal.kit.PropKit;
    7. import com.jfinal.log.Log;
    8. import com.momathink.common.constants.DictKeys;
    9. import com.momathink.common.service.ActiveRecordPluginService;
    10.  
    11. /**
    12.  * @ClassName: 访问者的ConfigName管理
    13.  * @author dufuzhong@126.com
    14.  * @date 2016年10月3日 下午2:33:33
    15.  */
    16. public class WebsiteInterceptor implements Interceptor {
    17.     private static final Log log = Log.getLog(WebsiteInterceptor.class);
    18.     
    19.     private static final ThreadLocal<String> ME_CONFIGNAME = new ThreadLocal<String>();
    20.     
    21.     public static final String SERVER_NAME = "serverName";
    22.     public static final String CONFIG_NAME = "configName";
    23.     
    24.     /***
    25.      * @Title: 获取访问者的 ConfigName
    26.      * @return String    本次访问的ConfigName
    27.      */
    28.     public static String getConfigName(){
    29.         return ME_CONFIGNAME.get();
    30.     }
    31.     
    32.     /* nginx 配置 内容:
    33.      
    34.             location xxxx {
    35.                 proxy_set_header Host $host:80;
    36.                 proxy_set_header X-Forwarded-For $remote_addr;
    37.                 proxy_redirect  off;
    38.                 proxy_pass http://127.0.0.1:8080;
    39.                 # 配置多台tomcat 就 改  端口号等
    40.             }
    41.         
    42.      */
    43.     
    44.     @Override
    45.     public void intercept(Invocation inv) {
    46.         Controller controller = inv.getController();
    47.         HttpServletRequest request = controller.getRequest();
    48.         
    49.         String ipFromNginx = controller.getHeader("X-Forwarded-For");
    50.         String serverName  = request.getServerName();
    51.         /*
    52.          如果你的数据源是已知固定的  在启动JFinal的时候 用 serverName 做数据源的 configName 也就是说他们是相等的, 
    53.          到这里就可以完结了, 如果是动态的,需要看后的动态管理方式
    54.          * */
    55.         // String configName = serverName;  下面的判断也 改成 if(DbKit.getConfig(configName) != null){
    56.         String configName  = ActiveRecordPluginService.me.getConfigName(serverName);
    57.         
    58.         controller.setAttr(SERVER_NAME, serverName);
    59.         controller.setAttr(CONFIG_NAME, configName);
    60.         
    61.         log.debug("访问者:  域名=" + serverName + "  资源K=" + configName + "   IP=" + ipFromNginx);
    62.         
    63.         if(configName != null){
    64.             
    65.             ME_CONFIGNAME.set(configName);
    66.             
    67.             //异常必须在里面的拦截器进行捕捉处理
    68.             inv.invoke();
    69.             
    70.             ME_CONFIGNAME.remove();
    71.             
    72.         } else {
    73.             controller.renderError(403);
    74.         }
    75.     }
    76.  
    77. }

    如果你的数据源是已知固定的  在启动JFinal的时候
     用 serverName 做数据源的 configName 也就是说他们是相等的,
     到这里就可以完结了, 如果是动态的,需要看后的动态管理方式

    动态管理:

    ActiveRecordPluginService管理控制

    1. import java.net.URLEncoder;
    2. import java.util.HashMap;
    3. import java.util.List;
    4. import java.util.Map;
    5.  
    6. import com.alibaba.fastjson.JSONArray;
    7. import com.alibaba.fastjson.JSONObject;
    8. import com.jfinal.kit.HttpKit;
    9. import com.jfinal.kit.JsonKit;
    10. import com.jfinal.kit.PropKit;
    11. import com.jfinal.kit.Ret;
    12. import com.jfinal.log.Log;
    13. import com.jfinal.plugin.IPlugin;
    14. import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
    15. import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
    16. import com.jfinal.plugin.activerecord.DbKit;
    17. import com.jfinal.plugin.activerecord.cache.EhCache;
    18. import com.jfinal.plugin.druid.DruidPlugin;
    19. import com.momathink.api.ApiInterceptor;
    20. import com.momathink.common.constants.DictKeys;
    21. import com.momathink.common.model.Site;
    22.  
    23. /**(域名白名单) 
    24.  * <br>
    25.  * 多站点配置信息和多数据源 管理
    26.  * @author dufuzhong@126.com
    27.  * @date 2016年11月8日 下午19:16:23
    28.  */
    29. public class ActiveRecordPluginService implements IPlugin {
    30.     
    31.     private static final Log log = Log.getLog(ActiveRecordPluginService.class);
    32.     
    33.     public static final ActiveRecordPluginService me = new ActiveRecordPluginService();
    34.  
    35.     /***
    36.      * 存储 多站点信息, 域名为 键, 数据源configName 为 值
    37.      */
    38.     private static final Map<String, String> CONFIGNAME_S = new HashMap<String, String>();
    39.     static{//初始化值
    40.         CONFIGNAME_S.put("localhost", DbKit.MAIN_CONFIG_NAME);
    41.         CONFIGNAME_S.put("127.0.0.1", DbKit.MAIN_CONFIG_NAME);
    42.     }
    43.     
    44.     private ActiveRecordPluginService(){}
    45.     public static ActiveRecordPluginService me() {
    46.         return me;
    47.     }
    48.     
    49.     /***
    50.      * @Title: 获取某站点 数据源configName
    51.      * @param key 访问者域名
    52.      * @return String    该站点数据源configName
    53.      */
    54.     public String getConfigName(String key) {
    55.         return CONFIGNAME_S.get(key);
    56.     }
    57.     
    58.     /***
    59.      * 加载 多站点配置信息
    60.      * @return 
    61.      */
    62.     public synchronized boolean load(List<Site> sites){
    63.         log.info("加载 多站点配置信息");
    64.         for (Site site : sites)
    65.             add(site);
    66.         
    67.         //报告记录一下启动情况
    68.         
    69.         String url = PropKit.get(DictKeys.SITE_SETCONFIGSITE).trim() + ApiInterceptor.getMaskKit();
    70.         try {
    71.             String data = URLEncoder.encode(JsonKit.toJson(sites), "UTF-8");
    72.             log.info("报告多站点多数据源配置信息网站地址: "+ url);
    73.             String post = HttpKit.post(url, "&data=" + data);
    74.             log.info("报告完毕:" + post);
    75.         } catch (Exception e) {
    76.             log.info("报告异常,可能是没有启动运维服务器, 异常信息:" + e.getMessage());
    77.             return false;
    78.         }
    79.         return true;
    80.     }
    81.      
    82.     /***
    83.      * 动态 配置 数据库参数 和 加载系统资源
    84.      */
    85.     public synchronized void add(Site site) {
    86.         log.info("解析内容:" +  site);
    87.         //加 try catch 的原因是 不能因为某个 站点 的错误 配置信息, 影响到 其他的站点
    88.         try {
    89.             //配置 数据库参数 和 加载系统资源
    90.             Ret addDb = init(site);
    91.             
    92.             //数据库启动载入, 启动成功则连接成功, 否则会异常
    93.             boolean druidPlugin = ((DruidPlugin) addDb.get("druidPlugin")).start();
    94.             boolean arp = ((ActiveRecordPlugin) addDb.get("arp")).start();
    95.             
    96.             //连接池和管理器都要成功true 才能通过
    97.             if(!(druidPlugin) || !(arp)) throw new Exception("连接池和管理器");
    98.             
    99.             //网址域名
    100.             String website = site.getWebsite().trim();
    101.             
    102.             //该网站的系统资源 KEY 值
    103.             String configname = site.getConfigName().trim();
    104.             
    105.             //存储 该站点资源KEY信息
    106.             CONFIGNAME_S.put(website, configname);
    107.             
    108.             //成功返回码
    109.             site.keep("id");
    110.             site.set(ApiInterceptor.ERRJSON_ERRCODE, 0).set(ApiInterceptor.ERRJSON_ERRMSG, "OK");
    111.             
    112.         } catch (Exception e) {
    113.             //错误码表 后面再写... 先直接 看错误信息吧
    114.             site.set(ApiInterceptor.ERRJSON_ERRCODE, 500).set(ApiInterceptor.ERRJSON_ERRMSG, e.getMessage());
    115.             
    116.             log.info("配置 数据库参数 错误信息:" +  site);
    117.         }
    118.     }
    119.     
    120.     /***
    121.      * 移除 动态 配置 数据库参数 和 加载的系统资源
    122.      */
    123.     public synchronized void del(Site site) {
    124.         CONFIGNAME_S.remove(site.getWebsite());
    125.         DbKit.removeConfig(site.getConfigName());
    126.         //成功返回码
    127.         site.keep("id");
    128.         site.set(ApiInterceptor.ERRJSON_ERRCODE, 0).set(ApiInterceptor.ERRJSON_ERRMSG, "OK");
    129.     }
    130.  
    131.     /**配置数据库参数 和 加载系统资源
    132.      */
    133.     private Ret init(Site site) {
    134.  
    135.         //该网站的系统资源 KEY 值
    136.         String configname = site.getConfigName().trim();
    137.         
    138.         String jdbcurl = site.getJdbcUrl().trim();
    139.         String user = site.getUser().trim();
    140.         String password = site.getPassword().trim();
    141.         
    142.         // 配置DruidPlugin数据库连接池插件
    143.         DruidPlugin druidPlugin = new DruidPlugin(jdbcurl, user, password);
    144.         
    145.         // 配置ActiveRecord插件
    146.         ActiveRecordPlugin arp = new ActiveRecordPlugin(configname, druidPlugin);
    147.         
    148.         //false 是大写, true是小写, 不写是区分大小写, 看老项目情况配置
    149.         arp.setContainerFactory(new CaseInsensitiveContainerFactory(false));
    150.         
    151.         //配置缓存类型
    152.         arp.setCache(new EhCache());
    153.         
    154.         return Ret.create("druidPlugin", druidPlugin).set("arp", arp);
    155.     }
    156.     
    157.  
    158.     @Override
    159.     public boolean start() {
    160.         //TODO 我的测试代码放在    F:workspacemoma_oa_test
    161.         String url = PropKit.get(DictKeys.SITE_GETCONFIGSITE).trim() + ApiInterceptor.getMaskKit();
    162.         log.info("获取多站点配置信息网站地址: "+ url);
    163.         String jsonStr =  HttpKit.get(url);
    164.         log.debug("获取的多站点配置: "+ jsonStr);
    165.         JSONObject json = JSONObject.parseObject(jsonStr);
    166.         
    167.         if(json.getInteger(ApiInterceptor.ERRJSON_ERRCODE) != 0){
    168.             log.info("错误信息:" + json.getString(ApiInterceptor.ERRJSON_ERRMSG));
    169.             return false;
    170.         }
    171.         
    172.         //解析json
    173.         List<Site> sites = JSONArray.parseArray(json.getString(ApiInterceptor.ERRJSON_DATA), Site.class);
    174.         
    175.         load(sites);
    176.         
    177.         return true;
    178.     }
    179.  
    180.     @Override
    181.     public boolean stop() {
    182.         //TODO 报告这些站点停了
    183.         return true;
    184.     }
    185.  
    186. }

    想动态那就要接口调用了:

    ConfigController:

    1. import com.alibaba.fastjson.JSONObject;
    2. import com.jfinal.aop.Before;
    3. import com.jfinal.aop.Clear;
    4. import com.momathink.common.annotation.controller.Controller;
    5. import com.momathink.common.base.BaseController;
    6. import com.momathink.common.model.Site;
    7. import com.momathink.common.service.ActiveRecordPluginService;
    8.  
    9. /***多站点 系统配置 操作API
    10.  * @author dufuzhong@126.com
    11.  * @date 2017年2月25日 下午1:59:07
    12.  */
    13. @Before({ApiInterceptor.class })
    14. @Controller(controllerKey = { "/api/config" })
    15. public class ConfigController extends BaseController {
    16.  
    17.     /** 动态管理数据库和系统资源接口
    18.      */
    19.     public void operation(){
    20.         String data = getPara("data");
    21.         Site site = JSONObject.parseObject(data, Site.class);
    22.         
    23.         if(site.isStateOn())
    24.             ActiveRecordPluginService.me.add(site);
    25.         
    26.         else if(site.isStateOff())
    27.             ActiveRecordPluginService.me.del(site);
    28.         
    29.         renderJson(ApiInterceptor.errJson(site));
    30.     }
    31.     
    32.   
    33.     
    34.     
    35. }

    接口 交接数据的 格式 规范:

    1. import com.jfinal.aop.Interceptor;
    2. import com.jfinal.aop.Invocation;
    3. import com.jfinal.core.Controller;
    4. import com.jfinal.kit.HashKit;
    5. import com.jfinal.kit.JsonKit;
    6. import com.jfinal.kit.StrKit;
    7. /**
    8.  * 接口 交接数据的 格式 规范
    9.  * 此拦截器仅做为示例展示,在本 demo 中 临时做一下 校验
    10.  */
    11. public class ApiInterceptor implements Interceptor {
    12.     
    13.     /**接口 返回值  错误码 K */
    14.     public static final String ERRJSON_ERRCODE = "errcode";
    15.     /**接口 返回值  错误信息 K */
    16.     public static final String ERRJSON_ERRMSG  = "errmsg";
    17.     /**接口 返回值  数据 K */
    18.     public static final String ERRJSON_DATA  = "data";
    19.     /**接口 调用数据时 使用的 K */
    20.     public static final String CHECK_MASK  = "mask";
    21.     
    22.     public void intercept(Invocation inv) {
    23.         Controller controller = inv.getController();
    24.         String mask = controller.getPara(CHECK_MASK);
    25.         if(isMask(mask)){
    26.             
    27.             try {
    28.                 inv.invoke();
    29.             } catch (Exception e) {
    30.                 inv.getController().renderJson(errJson(1, e.getMessage()));
    31.             }
    32.             
    33.         }else inv.getController().renderJson(errJson(401, "mask验证失败"));
    34.     }
    35.     
    36.     /**判断 密钥*/
    37.     private static boolean isMask(String mask){
    38.         if(StrKit.notBlank(mask))
    39.             return getMask().equals(mask.trim());
    40.         return false;
    41.     }
    42.     
    43.     
    44.     /***获取通行码
    45.      * 临时做一下 校验, 自行改造
    46.      */
    47.     public static String getMask(){
    48.         String mask = (new Date().getTime()+"").substring(0, 8) + "_dufuzhong@126.com";
    49.         //1.66665    分(min)
    50.         return HashKit.md5(mask);
    51.     }
    52.     
    53.     /***获取通行码
    54.      */
    55.     public static String getMaskKit(){
    56.         return "&"+ CHECK_MASK +"=" + getMask();
    57.     }
    58.     
    59.     
    60.     //--------------
    61.     
    62.     public static String errJson(Integer errcode, String errmsg) {
    63.         return "{"errcode":" + errcode + ","errmsg":"" + errmsg + ""}";
    64.     }
    65.     
    66.     public static String errJson(Object data) {
    67.         return "{"errcode":0,"errmsg":"OK","data":" + (JsonKit.toJson(data)) + "}";
    68.     }
    69.     
    70.     public static String errJson() {
    71.         return "{"errcode":0,"errmsg":"OK"}";
    72.     }
    73. }

    相关便利类;

    1. Site
    1. /**
    2.  * Generated by JFinal.
    3.  */
    4. @SuppressWarnings("serial")
    5. public class Site extends BaseSite<Site> {
    6.     public static final Site dao = new Site();
    7.     
    8.     /**关机**/
    9.     public static final Integer STATE_off = 0;
    10.     /**开机**/
    11.     public static final Integer STATE_on = 1;
    12.     /**操作中**/
    13.     public static final Integer STATE_out = 2;
    14.     
    15.     
    16.     public boolean isStateOn(){
    17.         return Site.STATE_on.equals(getState());
    18.     }
    19.     
    20.     public boolean isStateOff(){
    21.         return Site.STATE_off.equals(getState());
    22.     }
    23.     
    24. }
    1. /**
    2.  * Generated by JFinal, do not modify this file.
    3.  */
    4. @SuppressWarnings("serial")
    5. public abstract class BaseSite<extends BaseSite<M>> extends Model<M> implements IBean {
    6.  
    7.     public void setId(java.lang.Integer id) {
    8.         set("id", id);
    9.     }
    10.  
    11.     public java.lang.Integer getId() {
    12.         return get("id");
    13.     }
    14.  
    15.     public void setConfigName(java.lang.String configName) {
    16.         set("configName", configName);
    17.     }
    18.  
    19.     public java.lang.String getConfigName() {
    20.         return get("configName");
    21.     }
    22.  
    23.     public void setWebsite(java.lang.String website) {
    24.         set("website", website);
    25.     }
    26.  
    27.     public java.lang.String getWebsite() {
    28.         return get("website");
    29.     }
    30.  
    31.     public void setJdbcUrl(java.lang.String jdbcUrl) {
    32.         set("jdbcUrl", jdbcUrl);
    33.     }
    34.  
    35.     public java.lang.String getJdbcUrl() {
    36.         return get("jdbcUrl");
    37.     }
    38.  
    39.     public void setUser(java.lang.String user) {
    40.         set("user", user);
    41.     }
    42.  
    43.     public java.lang.String getUser() {
    44.         return get("user");
    45.     }
    46.  
    47.     public void setPassword(java.lang.String password) {
    48.         set("password", password);
    49.     }
    50.  
    51.     public java.lang.String getPassword() {
    52.         return get("password");
    53.     }
    54.  
    55.     public void setHostId(java.lang.Integer hostId) {
    56.         set("hostId", hostId);
    57.     }
    58.  
    59.     public java.lang.Integer getHostId() {
    60.         return get("hostId");
    61.     }
    62.  
    63.     public void setState(java.lang.Integer state) {
    64.         set("state", state);
    65.     }
    66.  
    67.     public java.lang.Integer getState() {
    68.         return get("state");
    69.     }
    70.  
    71.     public void setErrcode(java.lang.Integer errcode) {
    72.         set("errcode", errcode);
    73.     }
    74.  
    75.     public java.lang.Integer getErrcode() {
    76.         return get("errcode");
    77.     }
    78.  
    79.     public void setErrmsg(java.lang.String errmsg) {
    80.         set("errmsg", errmsg);
    81.     }
    82.  
    83.     public java.lang.String getErrmsg() {
    84.         return get("errmsg");
    85.     }
    86.  
    87. }

    到此基本完结了

  • 相关阅读:
    python实现的列表操作
    python的静态方法
    python标准库学习2
    javascript继承原型继承的例子
    jQuery高亮显示文本中重要的关键字
    表格展开伸缩
    jQuery设计思想
    python标准库学习3
    python中的继承和抽象类的实现
    表格的变色问题
  • 原文地址:https://www.cnblogs.com/zkwarrior/p/14964471.html
Copyright © 2020-2023  润新知