使用系统参数表,提升系统的灵活性

1、使用系统参数表的好处

​ Spring Boot项目中常有一些相对稳定的参数设置项,其作用范围是系统级的或模块级的,这些参数称为系统参数。这些变量以参数形式进行配置,从而提高变动和扩展的灵活性,保持代码的稳定性。

​ 以数据库表形式存储的系统参数表比配置文件(.properties文件或.yaml文件)要更灵活,因为无需重启系统就可以动态更新。

​ 系统参数表可用于存储下列数据:

  • 表字段枚举值,如下列字段:
question_type   TINYINT(4)   NOT NULL DEFAULT 0 COMMENT '题型,1-单选题,2-多选题,3-问答题',

​ 这个字段现在有3种取值,但是难保将来有扩展的可能,如:是非题、计算题、应用题等。

​ 因此将取值的枚举值用系统参数表来配置,可以提高系统扩展灵活性。

​ 另一方面,对于前端而言,就可以通过查询系统参数表数据,用于UI呈现,而不必硬编码。如前端需要用下拉框来显示所有可能的”题型”,这个列表就可以查询系统参数表来获取。

​ 因此可以将所有字段枚举值纳入系统参数表管理。

  • 参数设置,如邮件参数,对接的第三方系统的URL等。

2、系统参数表的表结构

​ 系统参数表的表结构如下:

DROP TABLE IF EXISTS sys_parameters;
CREATE TABLE sys_parameters
(
  class_id      INT(11)      NOT NULL DEFAULT 0 COMMENT '参数大类id',
  class_key     VARCHAR(60)  NOT NULL DEFAULT '' COMMENT '参数大类key',
  class_name    VARCHAR(60)  NOT NULL DEFAULT '' COMMENT '参数大类名称',
  item_id       INT(11)      NOT NULL DEFAULT 0 COMMENT '参数大类下子项id',
  item_key      VARCHAR(200) NOT NULL DEFAULT '' COMMENT '子项key',
  item_value    VARCHAR(200) NOT NULL DEFAULT '' COMMENT '子项值',
  item_desc     VARCHAR(512) NOT NULL DEFAULT '' COMMENT '子项描述',

  -- 记录操作信息
  login_name VARCHAR(80)  NOT NULL DEFAULT '' COMMENT '操作人账号',
  delete_flag   TINYINT(4)   NOT NULL DEFAULT 0 COMMENT '记录删除标记,1-已删除',
  create_time   DATETIME  NOT NULL DEFAULT NOW() COMMENT '创建时间',
  update_time   DATETIME           DEFAULT NULL ON UPDATE NOW() COMMENT '更新时间',
  PRIMARY KEY (class_id, item_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COMMENT '系统参数表';

​ 说明:

​ class_id字段只要确保一个参数大类(如一个枚举字段名)使用唯一值。使用class_key和item_key自动,便于提高记录数据和代码的可读性。class_key一般可以取字段名,但如果发生同名时,需要修改,确保不同表的同名字段,使用不同的class_key。对于枚举值类型,item_key可以取item_id相同的值,只是数据类型不同,此item_key转换成整型数,就是对应字段的值。

​ 这个表的数据一般可以由开发人员提供,包括初始或变动的SQL脚本,由DBA执行,项目无需为此开发界面来维护。

​ 下面是初始脚本示例:

INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (11, 'receive_flag', '短信接收标志', 0, '0', '未接收', '');
INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (11, 'receive_flag', '短信接收标志', 1, '1', '已接收', '');
INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (11, 'receive_flag', '短信接收标志', 2, '2', '发送失败', '');

INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (12, 'question_type', '题型', 1, '1', '单选题', '');
INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (12, 'question_type', '题型', 2, '2', '多选题', '');
INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (12, 'question_type', '题型', 3, '3', '问答题', '');

INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (101, 'url_param', 'URL参数', 0, 'url_prefix', 'http://questinvest.abc.com:8880', 'url前缀部分');
INSERT INTO sys_parameters(class_id, class_key, class_name, item_id, item_key, item_value, item_desc)
VALUES (101, 'url_param', 'URL参数', 1, 'url_action', '/questInvest/show', '请求接口方法');

3、系统参数表在项目中的使用

​ 在Spring Boot项目中,系统参数表一般只需在应用启动时加载一次,并提供更新接口允许管理员来更新数据。下面详细说明使用方法。

​ 先定义系统参数表的实体类,实体类为SysParameter,代码如下:

package com.abc.questInvest.entity;

import javax.persistence.Column;

import lombok.Data;

/**
 * @className   : SysParameter
 * @description : 系统参数信息对象类
 *
 */
@Data
public class SysParameter {
    //参数大类id
    @Column(name = "class_id")
    private Integer classId;

    //参数大类key
    @Column(name = "class_key")
    private String classKey;

    //参数大类名称
    @Column(name = "class_name")
    private String className;

    //子项id
    @Column(name = "item_id")
    private Integer itemId;

    //子项key
    @Column(name = "item_key")
    private String itemKey;

    //子项值
    @Column(name = "item_value")
    private String itemValue;

    //子项描述
    @Column(name = "item_desc")
    private String itemDesc;

    //========记录操作信息================
    // 操作人姓名
    @Column(name = "login_name")
    private String loginName;

    // 记录删除标记,保留
    @Column(name = "delete_flag")
    private Byte deleteFlag;

    // 创建时间
    @Column(name = "create_time")
    private Date createTime;

    // 更新时间
    @Column(name = "update_time")
    private Date updateTime;
}

​ 数据访问类为SysParameterDao,代码如下:

package com.abc.questInvest.dao;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import com.abc.questInvest.entity.SysParameter;

/**
 * @className   : SysParameterDao
 * @description : sys_parameters表数据访问类
 *
 */
@Mapper
public interface SysParameterDao {

    //查询所有系统参数,按class_id,item_id排序
    @Select("SELECT class_id,class_key,class_name,item_id,item_key,item_value,item_desc"
            + " FROM sys_parameters WHERE delete_flag = 0"
            + " ORDER BY class_id,item_id")
    List selectAll();
}

​ SysParameterDao类,使用Mybatis,只需提供查询接口就行了,因为修改在数据库后台执行了。当然如果项目方认为有必要提供界面来维护该表,则可增加相应CRUD的接口。

​ 服务接口类为SysParameterService,代码如下:

package com.abc.questInvest.service;

import java.util.List;

import com.abc.questInvest.entity.SysParameter;

/**
 * @className   : SysParameterService
 * @description : 系统参数数据服务
 *
 */
public interface SysParameterService {

    /**
     *
     * @methodName      : loadData
     * @description     : 加载数据库中数据,允许重复调用
     * @return          : 成功返回true,否则返回false。
     *
     */
    public boolean loadData();

    /**
     *
     * @methodName      : getParameterClass
     * @description     : 获取指定classKey的参数类别的子项列表
     * @param classKey  : 参数类别key
     * @return          : 指定classKey的参数类别的子项列表
     * @history     :
     * ------------------------------------------------------------------------------
     * date         version     modifier        remarks
     * ------------------------------------------------------------------------------
     * 2021/06/02   1.0.0       sheng.zheng     初版
     *
     */
    public List getParameterClass(String classKey);

    /**
     *
     * @methodName      : getParameterItemByKey
     * @description     : 根据classKey和itemKey获取参数子项
     * @param classKey  : 参数类别key
     * @param itemKey   : 子项key
     * @return          : SysParameter对象
     *
     */
    public SysParameter getParameterItemByKey(String classKey,String itemKey);

    /**
     *
     * @methodName      : getParameterItemByValue
     * @description     : 根据classKey和itemValue获取参数子项
     * @param classKey  : 参数类别key
     * @param itemValue : 子项值
     * @return          : SysParameter对象
     *
     */
    public SysParameter getParameterItemByValue(String classKey,String itemValue);
}

​ SysParameterService类定义了下列接口方法:

  • loadData方法,用于初始加载数据和更新数据。
  • getParameterClass方法,获取指定classKey的类别的所有子项列表。此方法调用会非常频繁。
  • getParameterItemByKey方法,根据classKey和itemKey获取参数子项,用于根据枚举值显示物理含义。此方法调用会非常频繁。
  • getParameterItemByValue方法,根据classKey和itemValue获取参数子项,用于根据物理含义取得枚举值。此方法调用会非常频繁。

​ 服务实现类为SysParameterServiceImpl,代码如下:

package com.abc.questInvest.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.abc.questInvest.dao.SysParameterDao;
import com.abc.questInvest.entity.SysParameter;
import com.abc.questInvest.service.SysParameterService;

import lombok.extern.slf4j.Slf4j;

/**
 * @className   : SysParameterServiceImpl
 * @description : SysParameterService实现类
 * @summary     : 实现对系统参数的管理
 *
 */
@Slf4j
@Service
public class SysParameterServiceImpl implements SysParameterService{
    //sys_parameters表数据访问对象
    @Autowired
    private SysParameterDao sysParameterDao;

    //管理全部的SysParameter表记录
    private Map> sysParameterMap = new HashMap>();

    /**
     *
     * @methodName      : loadData
     * @description     : 加载数据库中数据
     * @return          : 成功返回true,否则返回false。
     *
     */
    @Override
    public boolean loadData() {
        try
        {
            //查询sys_parameters表,获取全部数据
            List sysParameterList = sysParameterDao.selectAll();

            synchronized(sysParameterMap) {
                //先清空map,便于刷新调用
                sysParameterMap.clear();
                //将查询结果放入map对象中,按每个类别组织
                for(SysParameter item : sysParameterList) {
                    String classKey = item.getClassKey();
                    String itemKey = item.getItemKey();
                    Map sysParameterClassMap = null;
                    if (sysParameterMap.containsKey(classKey)) {
                        //如果存在该类别,则获取对象
                        sysParameterClassMap = sysParameterMap.get(classKey);
                    }else {
                        //如果不存在该类别,则创建
                        sysParameterClassMap = new HashMap();
                        //加入map中
                        sysParameterMap.put(classKey, sysParameterClassMap);
                    }
                    sysParameterClassMap.put(itemKey,item);
                }
            }
        }catch(Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     *
     * @methodName      : getParameterClass
     * @description     : 获取指定classKey的参数类别的子项列表
     * @param classKey  : 参数类别key
     * @return          : 指定classKey的参数类别的子项列表
     *
     */
    @Override
    public List getParameterClass(String classKey){
        List sysParameterList = new ArrayList();

        //获取classKey对应的子map,将所有子项加入列表中
        if (sysParameterMap.containsKey(classKey)) {
            Map sysParameterClassMap = sysParameterMap.get(classKey);
            for(SysParameter item : sysParameterClassMap.values()) {
                sysParameterList.add(item);
            }
        }

        return sysParameterList;
    }

    /**
     *
     * @methodName      : getParameterItemByKey
     * @description     : 根据classKey和itemKey获取参数子项
     * @param classKey  : 参数类别key
     * @param itemKey   : 子项key
     * @return          : SysParameter对象
     *
     */
    @Override
    public SysParameter getParameterItemByKey(String classKey,String itemKey) {
        SysParameter sysParameter = null;

        if (sysParameterMap.containsKey(classKey)) {
            //如果classKey存在
            Map sysParameterClassMap = sysParameterMap.get(classKey);
            if (sysParameterClassMap.containsKey(itemKey)) {
                //如果itemKey存在
                sysParameter = sysParameterClassMap.get(itemKey);
            }
        }

        return sysParameter;
    }

    /**
     *
     * @methodName      : getParameterItemByValue
     * @description     : 根据classKey和itemValue获取参数子项
     * @param classKey  : 参数类别key
     * @param itemValue : 子项值
     * @return          : SysParameter对象
     *
     */
    @Override
    public SysParameter getParameterItemByValue(String classKey,String itemValue) {
        SysParameter sysParameter = null;

        if (sysParameterMap.containsKey(classKey)) {
            //如果classKey存在
            Map sysParameterClassMap = sysParameterMap.get(classKey);
            //遍历
            for (Map.Entry item : sysParameterClassMap.entrySet()) {
                if(item.getValue().getItemValue().equals(itemValue)) {
                    //如果匹配值
                    sysParameter = item.getValue();
                    break;
                }
            }
        }

        return sysParameter;

    }
}

​ SysParameterServiceImpl类使用了Map

​ loadData方法,用于初始加载数据和更新时刷新数据,为了防止更新时脏读数据,加了同步锁。这个方法调用不频繁。

​ 全局配置服务类用于管理全局配置参数,包括系统参数、权限树等。如果只有一种参数,可以不必有此类,因为这样加了一层壳。

​ 服务接口类为GlobalConfigService,代码如下:

package com.abc.questInvest.service;

/**
 * @className   : GlobalConfigService
 * @description : 全局变量管理类
 *
 */
public interface GlobalConfigService {

    /**
     *
     * @methodName      : loadData
     * @description     : 加载数据
     * @return          : 成功返回true,否则返回false
     *
     */
    public boolean loadData();

    //获取SysParameterService对象
    public SysParameterService getSysParameterService();

    //获取其它配置数据服务对象
    //public FunctionTreeService getFunctionTreeService();
}

​ GlobalConfigService提供了下列接口方法:

  • loadData方法,加载配置对象数据,确定多个配置对象的加载次序。
  • getSysParameterService方法,获取系统参数服务类对象。
  • 获取其它可能的配置服务对象的方法。

​ 服务实现类为GlobalConfigServiceImpl,代码如下:

package com.abc.questInvest.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.abc.questInvest.service.FunctionTreeService;
import com.abc.questInvest.service.GlobalConfigService;
import com.abc.questInvest.service.RoleFuncRightsService;
import com.abc.questInvest.service.SysParameterService;
import com.abc.questInvest.service.TableCodeConfigService;

/**
 * @className   : GlobalConfigServiceImpl
 * @description : GlobalConfigService实现类
 *
 */
@Service
public class GlobalConfigServiceImpl implements GlobalConfigService{

    //系统参数表数据服务对象
    @Autowired
    private SysParameterService sysParameterService;

    //其它配置数据服务对象

    /**
     *
     * @methodName      : loadData
     * @description     : 加载数据
     * @return          : 成功返回true,否则返回false
     *
     */
    @Override
    public boolean loadData() {
        boolean bRet = false;

        //加载sys_parameters表记录
        bRet = sysParameterService.loadData();
        if (!bRet) {
            return bRet;
        }

        //加载其它配置数据

        return bRet;
    }

    //获取SysParameterService对象
    @Override
    public SysParameterService getSysParameterService() {
        return sysParameterService;
    }

    //获取其它配置数据服务对象方法

}

​ 全局配置服务类在应用启动时加载到Spring容器中,这样可实现共享,减少对数据库的访问压力。

​ 实现一个ApplicationListener类,代码如下:

package com.abc.questInvest;

import javax.servlet.ServletContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

import com.abc.questInvest.service.GlobalConfigService;

/**
 * @className   : ApplicationStartup
 * @description : 应用侦听器
 *
 */
@Component
public class ApplicationStartup implements ApplicationListener{
    //全局变量管理对象,此处不能自动注入
    private GlobalConfigService globalConfigService = null;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        try {
            if(contextRefreshedEvent.getApplicationContext().getParent() == null){
                //root application context 没有parent.

                System.out.println("========定义全局变量==================");
                // 将 ApplicationContext 转化为 WebApplicationContext
                WebApplicationContext webApplicationContext =
                        (WebApplicationContext)contextRefreshedEvent.getApplicationContext();
                // 从 webApplicationContext 中获取  servletContext
                ServletContext servletContext = webApplicationContext.getServletContext();

                //加载全局变量管理对象
                globalConfigService = (GlobalConfigService)webApplicationContext.getBean(GlobalConfigService.class);
                //加载数据
                boolean bRet = globalConfigService.loadData();
                if (false == bRet) {
                    System.out.println("加载全局变量失败");
                    return;
                }
                //======================================================================
                // servletContext设置值
                servletContext.setAttribute("GLOBAL_CONFIG_SERVICE", globalConfigService);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

​ 注意,globalConfigService不能自动注入,否则得到空指针。通过下列代码来加载bean。

//加载全局变量管理对象
                globalConfigService = (GlobalConfigService)webApplicationContext.getBean(GlobalConfigService.class);

​ 代码中,将globalConfigService对象作为全局变量加入ServletContext中,就可以实现共享了。

​ 在启动类中,加入该应用侦听器ApplicationStartup。

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(QuestInvestApplication.class);
        springApplication.addListeners(new ApplicationStartup());
        springApplication.run(args);
    }
        //获取ServletContext对象
        ServletContext servletContext = request.getServletContext();
        //获取全部数据服务对象
        GlobalConfigService globalConfigService = (GlobalConfigService)servletContext.getAttribute("GLOBAL_CONFIG_SERVICE");
        //获取系统参数url_prefix的值
        String url_prefix = "";
        SysParameter sysParameter = null;
        sysParameter = globalConfigService.getSysParameterService()
                .getParameterItemByKey("url_param", "url_prefix");
        if (sysParameter != null) {
            url_prefix = sysParameter.getItemValue();
        }

Original: https://www.cnblogs.com/alabo1999/p/14907461.html
Author: 阿拉伯1999
Title: 使用系统参数表,提升系统的灵活性

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/644370/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球