谈一谈自定义字段实现的几种方式
前言
我们经常会遇到项目中很多对表单进行自定义,比如说 saas 应用针对租户自定义表单字段名称,自定义列表名称。
还有更高级自定义,比如说自定义的模块,表单、字段、字段类型、流程等自定义。
提供自定义也是一个系统扩展性的体现,自定义功能的强大自然能适应更多的用户场景。
接下来我们就看看自定义的实现方案通常都有哪些方式。
常见的自定义字段的实现方式分为三种由简到繁,扩展性、复杂性也是逐渐增强的,每个方式各有优劣解决的场景也有所不同,具体往下看。
列式存储自定义字段(扩展字段 ext field)
模型如下:
ID | Name | Ext1(性别) | Ext2(地区) | Ext3(QQ) | Ext4(WECHAT) |
---|---|---|---|---|---|
1 | 韩梅梅 | 女 | Shanghai | 10000 | |
2 | 李磊 | 男 | Beijing | abc001 |
优点:
- 实现成本最低
- 可以直接表连接进行检索
缺点:
- 扩展能力一般,有上限
- 浪费资源,比如说有 20 个扩展字段,一行只用到 2 个,其余的 18 个都要存储 null 来浪费空间。
- 能解决的场景比较有限。
EAV 模型 Entity-Attribute-Value(实体、属性、值)
对象属性存储在一个有三列的表中:实体,属性和值(entity,attribute,value)。实体(entiry)表示所描述的数据项,例如一个产品或汽车。属性(attribute)表示描述实体的数据,例如一个产品将有价格,重量和许多其他属性。值(value)是属性的值,例如产品可能有一个 9.99 英镑的价格属性。此外值可以基于数据类型进行分割,所以可将 EAV 表分为字符串、整数、日期和长文本(long text)表。依据数据类型分割是为了支持索引,使得数据库执行可能的类型检查验证。
EAV 表模型带来了数据的灵活性,是的增加对象的属性不需要用增加数据库的字段,有很高的灵活性。但是 EAV 表也有较大的性能问题。通常,EAV 表带来的一个问题是当查找多个字段时,需要进行关联查询 join,这样的查询效率比较低。为了提高查询效率,我们可以对商品属性表进行矩阵转积处理(pivoting)。
一种方式是在代码中读出后存入 cache 中,当修改 attributes 表后触发更新 cache 或用 cron 定期更新;另一种方法是将关联信息组成一张大的临时表,数据的更新可以用数据库的触发器触发更新。由于大量数据在代码中进行处理会带来了 DB 的额外 IO 和服务器性能问题。当使用 EAV 表模型时,InnoDB 比 MYISAM 的性能要好不少。
注:我们常用的行模型(纵向)存储就是 EAV 模型实现的一种方式。
模型如下:
人员表(Entity)
ID | Name |
---|---|
1 | 韩梅梅 |
2 | 李磊 |
扩展映射(Entities)
Entity | Attribute | Value |
---|---|---|
1 | sex(性别) | 女 |
2 | sex(性别) | 男 |
1 | region(地区) | Shanghai |
2 | region(地区) | Beijing |
1 | 10000 | |
2 | abc001 |
优点:
- 扩展能力较强
- 理论上无上限
- 可以支持几乎所有的自定义字段的需求
缺点:
- 关联查询效率低下
- 需要维护自定义字段与值的关系表
Json 格式存储自定义字段
json 格式非常丰富,在描述自定义字段的这方面比较适合,可以把一行多列的数据压缩到一个 json text 内,也比较节省空间,json 格式可以无限扩展,还可支持多个自定义字段有不同的格式。
模型如下:
ID | Name | Content |
---|---|---|
1 | 韩梅梅 | {“sex”:”女”,”region”:”Shanghai”,”QQ”:”10000”} |
2 | 李磊 | {“sex”:”女”,”region”:”Beijing”,”WECHAT”:”abc001”} |
注:支持以上的两种不同的自定义格式并存
优点:
- 扩展能力强
- 理论上无上限
- 可以支持几乎所有的自定义字段的需求
- 无需维护自定义字段与值关系
缺点:
- 数据库需要支持 json type,不建议使用 text 类型
- 不支持关联查询(mongodb 除外)
- 自定义字段检索需要通过其他方式,例如搜索引擎。(mongodb 除外)
数据库对 Json 格式支持情况
数据库对 Json 类型的支持:
- Mysq5.7(CRUD 参考)
- PostgreSQL(CRUD 参考,json 与 jsonb 区别)
- MongoDB(CRUD 参考)
数据库对 json 类型的检索支持:
- Mysql5.7: 支持索引:通过虚拟列的功能可以对 JSON 中部分的数据进行索引。(相比 PG 和 MongoDB 弱一些,通过 json_extract()函数做一些简单查询)
- PostgreSQL:支持检索,可以复杂查询
- MongoDB:支持检索,可以复杂查询,支持 map reduce
ORM 框架对 Json 类型的支持:
- Mybatis 支持 json 格式字段映射到 POJO,方便 json 格式的 bean 与数据库映射。
- Hibernate 支持 json 格式字段映射到 POJO,方便 json 格式的 bean 与数据库映射。
Mysql5.7.x json 操作官方文档:
Mysql5.7.x 注意事项:
-
JSON_UNQUOTE 、->、->> 之间的区别
-
下面三个表达式返回相同的值
- JSON_UNQUOTE( JSON_EXTRACT(column, path) )
- JSON_UNQUOTE(column -> path)
- column->>path
-
-
JSON_CONTAINS_PATH 参数说明
-
第二个参数为
one
或all
的区别- one:至少存在一个路径返回 1,反之返回 0
- all:全部路径存在返回 1,反之返回 0
-
-
JSON_CONTAINS 参数说明
- 第二个参数是不接受整数的,无论 json 元素是整型还是字符串,否则会出现这个错误
-
5.7.x 不同版本支持的程度:
-
MySQL 5.7.13
- 支持操作符 ->>
-
MySQL 5.7.9
- 支持操作符 -> (JSON_EXTRACT()函数别名)
- 重命名函数 JSON_APPEND()为 JSON_ARRAY_APPEND(),函数作用:将值追加到 JSON 文档中指定数组的末尾并返回结果,未来会删除’JSON_APPEND()’
-
MySQL 5.7.22
- 支持 JSON_ARRAYAGG()返回 json 数组形式结果集,JSON_OBJECTAGG()返回 kson 对象形式结果集
- 添加 JSON_MERGE_PATCH(),作用:合并结果(相同 path)
- 添加 JSON_MERGE_PRESERVE(),作用:合并数据(不同 path)
- 弃用 JSON_MERGE(),使用 JSON_MERGE_PRESERVE() / JSON_MERGE_PATCH(),未来会删除’JSON_MERGE()’
-
实现方式不局限于上面说到的方式,有更好的方式欢迎留言进行沟通。
真诚点赞 诚不我欺~