AJ 组件库之通用数据字典 DataDict

Posted sp42a

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AJ 组件库之通用数据字典 DataDict相关的知识,希望对你有一定的参考价值。

需求分析

数据字典(Data Dictionary)是一种用户可以访问的记录数据库和应用程序元数据的目录。数据字典在系统也是必备的组件,对此我们想想要怎么做:

  • 基本功能:Key/Value 方式的存储结构,外加其他属性如”说明“,尽可能设计为通用一点
  • 树状结构,也就是说带分类功能的。换句话说,这个数据字典也是一个分类表(数据库层面就是加一个 parentId 字段,当然其他 逻辑较为复杂,而且也不是 pId 这唯一一种树的存储方法)
  • 可以租户/门户隔离,也就是多个门户或者租户。

基本设计

表设计如下。

CREATE TABLE `sys_datadict` (
	`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键 id,自增',
	`name` VARCHAR(20) NULL DEFAULT NULL COMMENT '名称、自定义编码、相当于 key。可选的' COLLATE 'utf8mb4_bin',
	`value` VARCHAR(50) NOT NULL COMMENT '值' COLLATE 'utf8mb4_bin',
	`content` VARCHAR(200) NULL DEFAULT NULL COMMENT '简介、描述' COLLATE 'utf8mb4_bin',
	`parentId` INT(11) NULL DEFAULT NULL COMMENT '父 id',
	`type` INT(11) NULL DEFAULT NULL COMMENT '类型 id',
	`sortNo` TINYINT(4) NULL DEFAULT NULL COMMENT '顺序、序号',
	`stat` TINYINT(4) NULL DEFAULT NULL COMMENT '数据字典:状态',
	`uid` BIGINT(20) NULL DEFAULT NULL COMMENT '唯一 id,通过 uuid 生成不重复 id',
	`tenantId` INT(11) NOT NULL DEFAULT '0' COMMENT '租户 id。0 = 不设租户',
	`createByUser` INT(11) NULL DEFAULT NULL COMMENT '创建者 id',
	`createDate` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
	`updateDate` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
	PRIMARY KEY (`id`) USING BTREE,
	INDEX `tenantId` (`tenantId`) USING BTREE
)
COMMENT='数据字典'
COLLATE='utf8mb4_bin';

Java Bean:

import java.util.Date;

import com.ajaxjs.framework.IBaseModel;

/**
 * 数据字典 
 * 
 * @author Frank Cheung<sp42@qq.com>
 * @date 2021-11-07
 */
public class DataDict implements IBaseModel 
    /**
     * 主键 id,自增
     */
    private Long id;

    /**
     * 名称、自定义编码、相当于 key。可选的
     */
    private String name;

    /**
     * 值
     */
    private String value;

    /**
     * 简介、描述
     */
    private String desc;

    /**
     * 父 id
     */
    private Long parentId;

    /**
     * 类型 id
     */
    private Long type;

    /**
     * 顺序、序号
     */
    private Integer sortNo;

    /**
     * 数据字典:状态
     */
    private Integer stat;

    /**
     * 唯一 id,通过 uuid 生成不重复 id
     */
    private Long uid;

    /**
     * 租户 id。0 = 不设租户
     */
    private Long tenantId;

    /**
     * 创建者 id
     */
    private Long createByUser;

    /**
     * 创建时间
     */
    private Date createDate;

    /**
     * 修改时间
     */
    private Date updateDate;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getValue() 
        return value;
    

    public void setValue(String value) 
        this.value = value;
    

    public String getDesc() 
        return desc;
    

    public void setDesc(String desc) 
        this.desc = desc;
    

    public Long getParentId() 
        return parentId;
    

    public void setParentId(Long parentId) 
        this.parentId = parentId;
    

    public Long getType() 
        return type;
    

    public void setType(Long type) 
        this.type = type;
    

    public Integer getSortNo() 
        return sortNo;
    

    public void setSortNo(Integer sortNo) 
        this.sortNo = sortNo;
    

    public Integer getStat() 
        return stat;
    

    public void setStat(Integer stat) 
        this.stat = stat;
    

    public Long getUid() 
        return uid;
    

    public void setUid(Long uid) 
        this.uid = uid;
    

    public Long getTenantId() 
        return tenantId;
    

    public void setTenantId(Long tenantId) 
        this.tenantId = tenantId;
    

    public Long getCreateByUser() 
        return createByUser;
    

    public void setCreateByUser(Long createByUser) 
        this.createByUser = createByUser;
    

    public Date getCreateDate() 
        return createDate;
    

    public void setCreateDate(Date createDate) 
        this.createDate = createDate;
    

    public Date getUpdateDate() 
        return updateDate;
    

    public void setUpdateDate(Date updateDate) 
        this.updateDate = updateDate;
    

界面:


UI 设计也是大头问题,我们后面再讲。

实现难点

树状结构存储

如何在关系型数据库里面存储树状结构一直是需要思考的问题,尤其对于数据结构掌握不好的同学来说,更是难点。这个知识点我前期探索过,参考博文《SQL 双亲节点查找所有子节点》。现在回过头来看,结合大家的一些经验,决定这样子做。

  • mysql 还是老老实实用 parentId 的存储方法
  • MySQL 8.0 虽然支持 CTE 递归,但老版本就无能为力了。对此,比较简单省力的方式是,直接上 MySQL 函数。

MySQL 函数为 getAllChildren,源码如下:

BEGIN
	DECLARE P_TEMP VARCHAR(1000);
	DECLARE C_TEMP VARCHAR(1000);
	
	SET P_TEMP = '0';
	SET C_TEMP = CAST(rootId AS CHAR);
	
	WHILE C_TEMP IS NOT NULL DO
		SET P_TEMP = CONCAT(P_TEMP, ',', C_TEMP);
		
		IF tableName = 'sys_datadict' THEN
			SELECT GROUP_CONCAT(id) INTO C_TEMP FROM sys_datadict WHERE FIND_IN_SET(parentId, C_TEMP) > 0; 
		ELSEIF tableName = 'sys_datadict1' THEN
			SELECT GROUP_CONCAT(id) INTO C_TEMP FROM sys_datadict WHERE FIND_IN_SET(parentId, C_TEMP) > 0; 
		ELSE 
			SET C_TEMP = NULL;
		END IF; -- if结束
	END WHILE; 
	 
	RETURN P_TEMP; 
END

调用例子:

SELECT * FROM sys_datadict WHERE FIND_IN_SET(id, getAllChildren(2, 'sys_datadict')) ORDER BY parentId ASC

关键原理是 MySQL 的 find_in_set 函数,顺带学习一下。当然了——换了别的数据库就不是这样子做,通通要改。

getAllChildren() 第一个参数为 INT,是父亲 id,第二个参数是表名,——为什么有表名呢?因为 MySQL 函数不支持动态传入表名作为参数,于是只能写死,因为当初我想这个函数支持多张表而不是一张表写一个函数的。不过我采取代价相对较低的”写死“,在函数里面写死表名,通过 IF ELSE 判断,便可达到支持多张表的目的。

调用上述 SQL 成功返回如下。

树节点数组如何转换为 JSON 树?

数据库返回的是一维数组,如何转换为带层次的 JSON 树呢?首先声明一个树节点的类型。

/**
 * 树节点
 */
type TreeMap = 
    id: number;

    /**
     * 父 id,输入时候必填
     */
    parentId: number;

    /**
     * 子节点,生成 Tree 之后出现
     */
    children?: TreeMap[];

输入(JSON 起码要有 idparentId 字段):

"result":["stat":null,"updateDate":"2022-03-28 18:12:55","type":null,"content":null,"parentId":9,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":14,"value":"广州新闻","createByUser":null,"createDate":"2022-03-28 18:12:26", "stat":null,"updateDate":"2022-02-26 21:38:28","type":null,"content":null,"parentId":5,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":6,"value":"香港新闻","createByUser":null,"createDate":"2022-02-26 21:38:28", "stat":null,"updateDate":"2022-03-28 15:20:43","type":null,"content":null,"parentId":5,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":7,"value":"澳门新闻","createByUser":null,"createDate":"2022-03-28 15:20:43", "stat":null,"updateDate":"2022-03-28 15:20:55","type":null,"content":null,"parentId":5,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":8,"value":"台湾新闻","createByUser":null,"createDate":"2022-03-28 15:20:55", "stat":null,"updateDate":"2022-03-28 15:22:06","type":null,"content":null,"parentId":4,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":12,"value":"美国新闻","createByUser":null,"createDate":"2022-03-28 15:22:06", "stat":null,"updateDate":"2022-03-28 15:22:14","type":null,"content":null,"parentId":4,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":13,"value":"俄罗斯新闻","createByUser":null,"createDate":"2022-03-28 15:22:14", "stat":null,"updateDate":"2022-03-28 15:21:08","type":null,"content":null,"parentId":3,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":9,"value":"广东新闻","createByUser":null,"createDate":"2022-03-28 15:21:05", "stat":null,"updateDate":"2022-03-28 15:21:23","type":null,"content":null,"parentId":3,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":10,"value":"湖南新闻","createByUser":null,"createDate":"2022-03-28 15:21:23", "stat":null,"updateDate":"2021-11-06 23:58:11","type":null,"content":null,"parentId":2,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":3,"value":"国内新闻","createByUser":null,"createDate":"2021-11-06 23:58:11", "stat":null,"updateDate":"2021-11-06 23:58:25","type":null,"content":null,"parentId":2,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":4,"value":"国际新闻","createByUser":null,"createDate":"2021-11-06 23:58:25", "stat":null,"updateDate":"2021-11-06 23:58:52","type":null,"content":null,"parentId":2,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":5,"value":"港澳台新闻","createByUser":null,"createDate":"2021-11-06 23:58:52", "stat":null,"updateDate":"2021-11-06 23:57:25","type":null,"content":null,"parentId":1,"sortNo":null,"uid":null,"name":null,"tenantId":0,"id":2,"value":"新闻","createByUser":null,"createDate":"2021-11-06 23:57:25"]

转换为(多了 children,表示下一级的节点集合):

[
	"stat": null,
	"updateDate": "2021-11-06 23:58:11",
	"type": null,
	"content": null,
	"parentId": 2,
	"sortNo": null,
	"uid": null,
	"name": null,
	"tenantId": 0,
	"id": 3,
	"value": "国内新闻",
	"createByUser": null,
	"createDate": "2021-11-06 23:58:11",
	"children": [
		"stat": null,
		"updateDate": "2022-03-28 15:21:08",
		"type": null,
		"content": null,
		"parentId": 3,
		"sortNo": 《翻转组件库之卡片设计》

《翻转组件库之卡片设计》

6种innodb数据字典恢复方法

如何从 python 中的字典创建 ndjson 对象?

94存储库之MongoDBmysql

python标准库之collections