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 起码要有 id
、parentId
字段):
"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": 《翻转组件库之卡片设计》