详解Spring Boot使用系统参数表提升系统的灵活性


Posted in Java/Android onJune 30, 2021
目录
  • 一、使用系统参数表的好处
  • 二、系统参数表的表结构
  • 三、系统参数表在项目中的使用
    • 3.1、Entity类
    • 3.2、Dao类
    • 3.3、Service类
    • 3.4、ServiceImpl类
    • 3.5、全局配置服务类
    • 3.6、启动时加载
    • 3.7、在服务实现类中访问系统参数

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

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

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

表字段枚举值,如下列字段:

`question_type`   TINYINT(4)   NOT NULL DEFAULT 0 COMMENT '题型,1-单选题,2-多选题,3-问答题',

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

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

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

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

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

二、系统参数表的表结构

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

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', '请求接口方法');

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

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

3.1、Entity类

​先定义系统参数表的实体类,实体类为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;	
}

3.2、Dao类

​数据访问类为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<SysParameter> selectAll();
}

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

3.3、Service类

​服务接口类为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的参数类别的子项列表
	 *
	 */
	public List<SysParameter> 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获取参数子项,用于根据物理含义取得枚举值。此方法调用会非常频繁。

3.4、ServiceImpl类

​服务实现类为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<String,Map<String,SysParameter>> sysParameterMap = new HashMap<String,Map<String,SysParameter>>();
	
	/**
	 * 
	 * @methodName		: loadData
	 * @description		: 加载数据库中数据 
	 * @return			: 成功返回true,否则返回false。
	 *
	 */	
	@Override
	public boolean loadData() {
		try
		{
			//查询sys_parameters表,获取全部数据
			List<SysParameter> sysParameterList = sysParameterDao.selectAll();
			
			synchronized(sysParameterMap) {
				//先清空map,便于刷新调用
				sysParameterMap.clear();
				//将查询结果放入map对象中,按每个类别组织
				for(SysParameter item : sysParameterList) {
					String classKey = item.getClassKey();
					String itemKey = item.getItemKey();
					Map<String,SysParameter> sysParameterClassMap = null;
					if (sysParameterMap.containsKey(classKey)) {
						//如果存在该类别,则获取对象
						sysParameterClassMap = sysParameterMap.get(classKey);
					}else {
						//如果不存在该类别,则创建
						sysParameterClassMap = new HashMap<String,SysParameter>();
						//加入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<SysParameter> getParameterClass(String classKey){
		List<SysParameter> sysParameterList = new ArrayList<SysParameter>();
		
		//获取classKey对应的子map,将所有子项加入列表中
		if (sysParameterMap.containsKey(classKey)) {
			Map<String,SysParameter> 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<String,SysParameter> 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<String,SysParameter> sysParameterClassMap = sysParameterMap.get(classKey);
			//遍历
			for (Map.Entry<String,SysParameter> item : sysParameterClassMap.entrySet()) {
				if(item.getValue().getItemValue().equals(itemValue)) {
					//如果匹配值
					sysParameter = item.getValue();
					break;
				}
			}
		}
		
		return sysParameter;
		
	}
}

​SysParameterServiceImpl类使用了Map<String,Map<String,SysParameter>>类型的属性变量sysParameterMap来管理全部的系统参数,外层Map管理classKey到Map<String,SysParameter>的映射关系,每一项为一个参数类别,而里层Map<String,SysParameter>,用于管理itemKey与SysParameter之间的映射关系,每一项为该类别下的一个子项。使用sysParameterMap属性的目的,是将所有系统参数都加载到内存中,从而无需频繁访问数据库。

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

3.5、全局配置服务类

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

​服务接口类为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;
	}
	
	//获取其它配置数据服务对象方法
	
}

3.6、启动时加载

​全局配置服务类在应用启动时加载到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<ContextRefreshedEvent>{
    //全局变量管理对象,此处不能自动注入
    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);  
}

3.7、在服务实现类中访问系统参数

​HttpServletRequest类型对象request在控制器方法中可以获取,可作为参数传入服务实现类的方法中。下面是服务实现类访问系统参数的示例代码:

//获取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();
}

以上就是详解Spring Boot使用系统参数表提升系统的灵活性的详细内容,更多关于Spring Boot使用系统参数表提升系统的灵活性的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
SpringCloud Alibaba 基本开发框架搭建过程
Jun 13 Java/Android
解决SpringCloud Feign传对象参数调用失败的问题
Jun 23 Java/Android
Java中PriorityQueue实现最小堆和最大堆的用法
Jun 27 Java/Android
java固定大小队列的几种实现方式详解
Jul 15 Java/Android
SpringBoot中使用Redis作为全局锁示例过程
Mar 24 Java/Android
Java中API的使用方法详情
Apr 06 Java/Android
Java 超详细讲解ThreadLocal类的使用
Apr 07 Java/Android
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
Apr 13 Java/Android
Elasticsearch 配置详解
Apr 19 Java/Android
Spring Data JPA框架的核心概念和Repository接口
Apr 28 Java/Android
Java死锁的排查
May 11 Java/Android
IDEA中sout快捷键无效问题的解决方法
Jul 23 Java/Android
浅谈resultMap的用法及关联结果集映射
Spring中bean的生命周期之getSingleton方法
每日六道java新手入门面试题,通往自由的道路
Jun 30 #Java/Android
mybatis 解决从列名到属性名的自动映射失败问题
Jun 30 #Java/Android
Java基础之this关键字的使用
Jun 30 #Java/Android
Java图书管理系统,课程设计必用(源码+文档)
详解Java ES多节点任务的高效分发与收集实现
Jun 30 #Java/Android
You might like
第十三节--对象串行化
2006/11/16 PHP
PHP语法自动检查的Vim插件
2014/08/11 PHP
php将图片保存为不同尺寸图片的图片类实例
2015/03/30 PHP
jquery的ajax请求全面了解
2013/03/20 Javascript
js如何取消事件冒泡
2013/09/23 Javascript
javascript动态的改变IFrame的高度实现自动伸展
2013/10/12 Javascript
javascript阻止浏览器后退事件防止误操作清空表单
2013/11/22 Javascript
JS取request值以及自动执行使用示例
2014/02/24 Javascript
jQuery 实现ajax传入参数含有特殊字符的方法总结
2016/10/17 Javascript
详解angularjs中的隔离作用域理解以及绑定策略
2017/05/31 Javascript
浅谈vue实现数据监听的函数 Object.defineProperty
2017/06/08 Javascript
详解如何使用webpack在vue项目中写jsx语法
2017/11/08 Javascript
微信小程序如何引用外部js,外部样式,公共页面模板
2019/07/23 Javascript
Vue实现点击箭头上下移动效果
2020/06/11 Javascript
Vue如何实现验证码输入交互
2020/12/07 Vue.js
vue-cli中实现响应式布局的方法
2021/03/02 Vue.js
[38:38]完美世界DOTA2联赛PWL S3 access vs Rebirth 第二场 12.17
2020/12/18 DOTA
python获取当前时间对应unix时间戳的方法
2015/05/15 Python
Python的Django框架中的数据库配置指南
2015/07/17 Python
python遍历 truple list dictionary的几种方法总结
2016/09/11 Python
python语言中with as的用法使用详解
2018/02/23 Python
解决Keras的自定义lambda层去reshape张量时model保存出错问题
2020/07/01 Python
Pycharm 设置默认解释器路径和编码格式的操作
2021/02/05 Python
通过css3动画和opacity透明度实现呼吸灯效果
2019/08/09 HTML / CSS
ECCO爱步美国官网:来自丹麦的鞋履品牌
2016/11/23 全球购物
俄罗斯皮肤健康中心:Pharmacosmetica.ru
2020/02/22 全球购物
模具专业推荐信
2013/10/30 职场文书
会计系个人求职信范文分享
2013/12/20 职场文书
中西医专业毕业生职业规划书
2014/02/24 职场文书
《草原的早晨》教学反思
2014/04/08 职场文书
护理学专业求职信
2014/06/29 职场文书
2014年出纳工作总结与计划
2014/12/09 职场文书
党建工作目标管理责任书
2015/01/29 职场文书
幼儿园个人总结
2015/02/28 职场文书
Python OpenCV实现传统图片格式与base64转换
2021/06/13 Python
Python实战之大鱼吃小鱼游戏的实现
2022/04/01 Python