一般我们在开发的时候,习惯上使用常规的关系型数据库来设计数据库表,对于一些业务表的字段比较固定的场景,是一种非常不错的选择,而且查询的时候,由于是基于固定的表字段进行查询,性能基本上是最优的。不过有一些场景下,业务信息的经常变化,使用常规的关系型数据库来创建表字段、删除字段的模式,肯定不是合适的处理方案,因此可能会进入JSON数据存储的方式,而现今很多关系型数据库也都支持JSON的存储和子查询处理,不过JSON的检索还是比较麻烦,而且对于复杂的子查询,性能据说也好不到哪里。而非关系型数据库的NoSQL数据库(MongoDB数据库),它的产生就是为了解决大规模数据集合多重数据种类带来的挑战。结合关系型数据库的熟练使用、性能优势和MongoDB数据库的弹性化文档处理特点,我对EAV模型(实体-属性-值)的设计和低代码的处理方案提供一个实用的思路供参考。

1、数据库 EAV 模型设计

如果我们要做一个电商的商品管理,我们先卖一些衣服,需要管理衣服的尺码、颜色、款式等信息;有一天需要卖电脑了,电脑需要 主板、CPU、显卡、内存、硬盘、散热 等信息;过几天又需要卖手机了,手机有 颜色、版本、存储容量、套餐类型等等信息。数据库的频繁更改,可能会导致开发的复杂度增加,说不准要重新处理。

如果我们每次新增商品,需要支持不同的信息的话就不停的加字段。这样会导致很多问题:

1、实现成本高,每次可能需要重新修改底层代码,界面布局及相关处理等

2、性能越来越差,每次增加的字段,随着复杂性越来越大,字段越来越多,导致性能急剧下降。

3、维护越来越难,没有好的系统规划,就如在沙堆上建设豪华城堡,随着时间的推移,越来越难维护。

还有一种是采用JSON数据存储方案,对于扩展的数据,可以统一存储在某个JSON里面,这样设计的扩展字段,可以有效屏蔽一些复杂度,并提高弹性化。如可以把 尺码、颜色、款色、主板、CPU、显卡、内存等等都放到 JSON 里。

不过这样JSON数据存储方案,对于更新和条件查询来说,性能是比较差的,随着数据量和复杂度的增加,这种响应效果肯定不如意的,对于大数据的处理,这种处理肯定是灾难性的。

EAV(Entity-Attribute-Value)模型是一种灵活的数据库设计方法,特别适合存储具有可变属性的实体。

  • Entity:实体,代表一个业务对象,比如上面的例子里的商品。
  • Attribute:对象的属性,属性并不是作为实体单独的一列来进行存放,而是存储在一组单独的数据库表中。
  • Value:指特定属性所关联的值。

在电商商品管理系统中,商品的属性可能会变化,因此EAV模型是一个合适的选择。每个实体都有唯一的标识符,每个实体都可以有多个属性与之关联,每个属性都有唯一的标识符,每个属性都可以具有多个值。

以下是一个简单的EAV模型设计示例:

实体表(Entity Table):

这里的实体是指商品。可能这里用实体类型表述更准确。

属性表(Attribute Table):

这里的属性是指商品的特征,例如颜色、尺寸等。

值表(Value Table):

这里存储了实体和属性的关联,以及具体的属性值。

这样设计的好处在于,你可以灵活地添加新的属性,而无需修改数据库结构。然而,EAV模型也有一些缺点,例如查询可能会更加复杂,因为需要在值表中进行属性值的连接。

2、优化的EAV模型

对于上面属性值的存储,统一都采用字符串的方式来存储,这样对于类型的处理和空间节约肯定是不好的,因此我们需要进行优化,根据不同的类型存储在不同的表上。

上面属性值没有类型限制,都是 VARCHAR 的,对数据库不友好,会导致内存浪费,而且存取都需要进行数据格式转换。对存储为字符串的值创建的索引不允许针对数值型和日期型的搜索范围优化,这是采用混合数据类型的键-值对描述数据的公共问题。

我们对属性值表基于数据类型进行分割,每个不同的数据类型拆为一个单独的表,同时通过 属性表(Attribute) 添加 类型决定去哪里存取数据。

我们可以借鉴magento的eav模型,它是EAV设计的最优参考了。Magento 2中的EAV属性类型有下面这些表:

  • eav_entity_int
  • eav_entity_varchar
  • eav_entity_text
  • eav_entity_decimal
  • eav_entity_datetime

这5种属性类型就相当于字段类型,一般关系型数据库类型是通用的。

  • int 对应字段的int类型
  • varchar 对应字段的varchar类型
  • text 对应字段的text类型
  • decimal 对应字段的decimal类型
  • datetime对应字段的datetime类型

这样分别不同类型的数据进行不同表的存储了。

其他的属性类似的处理即可。

参考下eav的设计图,了解一下各个表之间的关系。

以及magento的eav模型设计图,复杂的令人抓狂。

3、NoSQL数据库的登场

使用EAV(Entity-Attribute-Value)模式来存储完整的数据结构信息以及NoSQL数据库来存储完整的记录是一种灵活的方法,特别适用于需要存储动态结构数据的场景。

EAV的常规关系型数据库表存储常规的设计表,如实体类型、属性定义、属性值(多个)表的相关信息,而利用MongoDB数据库的大数据处理灵活性和高性能的响应,能够存储我们实际变化的文档信息。在检索的时候,并提供了常规关系型数据库的联合查询、JSON查询无法得到的灵活性和高性能。好马配好鞍,双剑合璧,简直完美。

在介绍实现我们的EAV模型设计的过程前,我们先来看看实际的界面效果

1)实体类型表和属性定义处理

我们新增实体类型的时候,只需要填写简单的信息和类名即可,如果对于产品的定义。

属性定义,除了指定属性的一些名称、排序、默认值、属性值存储类型外,还可以设置是否为字典列表、或者从其他类型表中选择等处理。

有了字段的定义,我们就可以在业务列表中显示相关的字段,并从MongoDB总检索指定类型的数据,由于MongoDB本身支持非常好的查询处理,因此对于查询来说非常简单。

如对于产品定义和数据展示来说,我们动态创建的菜单,根据实体类型的ID就可以进行通用的查询了。如下界面所示。

这个表的数据在MongoDB中存储的,如下界面所示。

对于有主从表的业务处理,也是同样的处理方式,除了显示主表的信息外,还需要展示明细的记录数据,我们通过整合关系型数据库的EAV表和MongoDB的文档记录显示,就可以很好的展示相关的数据了。

我们在订单明细表中选择表的设置,我们可以再明细表格中动态进行数据的选择处理, 并可以设置关联复制的属性字段,如下界面所示。

订单明细表的产品名称属性信息定义如下所示。

因为订单明细表中,有时候需要复制来自产品信息的一些字段,我们在按钮【设置其他复制字段】中处理映射关系即可。

这样就可以自动引入选择表的属性值来填充了。

以上就是针对EAV模型设计,以及引入MongoDB来存储详细数据记录,以便高效的查询数据和处理动态化字段内容的需求。

有时间会继续写文章介绍详细的实现过程,以及界面的动态化处理模式。

EAV模型(实体-属性-值)的设计和低代码的处理方案(1)的更多相关文章

  1. SQL反模式学习笔记6 支持可变属性【实体-属性-值】

    目标:支持可变属性 反模式:使用泛型属性表.这种设计成为实体-属性-值(EAV),也可叫做开放架构.名-值对. 优点:通过增加一张额外的表,可以有以下好处 (1)表中的列很少: (2)新增属性时,不需 ...

  2. IOS开发之动态获取模型的属性值

    #import "model.h" #import <objc/runtime.h> @implementation model +(instancetype)mode ...

  3. EAV模型

    了解EAV设计基本原理的最好方法就是理解行建模(row modelling,其中EAV是广义形式). 以一超市数据库为例,必须管理数以千计的产品和品牌,其中许多产品存在期很短暂.那么,显而易见,产品名 ...

  4. java怎么比较两个实体类的属性值

    分享一下比较两个实体类的工具包 package cn.mollie.utils; import java.beans.Introspector; import java.beans.PropertyD ...

  5. 反射 实现不同模型相同属性赋值 第二集(automapper)

    前言: 两年前写过一个 反射实现不同模型相同属性赋值 只能简单的实现两个model 相同属性名,相同类型赋值 最近又遇到这个问题,需要对相同属性名或者指定属性名 不同类型(复杂对象,如:List< ...

  6. Java反射之Bean修改更新属性值等工具类

    package com.bocean.util; import java.lang.annotation.Annotation; import java.lang.reflect.Field; imp ...

  7. webdriver高级应用- 改变一个页面对象的属性值

    适用于一些无法操作的元素,可以直接改他的属性从而操作,代码如下: #encoding=utf-8 from selenium import webdriver import unittest impo ...

  8. selenium--更改标签的属性值

    前戏 在进行web自动化的时候,我们有时需要获取元素的属性,有时需要添加,有时需要删除,这时候就要通过js来进行操作了 实战 from selenium import webdriver import ...

  9. webdriver高级应用 -更改一个对象界面的属性值

    #-*- coding=utf-8 -*- #更改一个对象界面的属性值 from selenium import webdriver import unittest def addAttribute( ...

  10. DDD 领域驱动设计-三个问题思考实体和值对象(续)

    上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...

随机推荐

  1. #斜率优化,二分#CF631E Product Sum

    题目 有一个数列 \(a\),其权值为 \(\sum_{i=1}^ni*a_i\), 现在可以任意选择其中一个数字扔到任意位置,使权值和最大. \(n\leq 2*10^5,|a_i|\leq 10^ ...

  2. CentOS 9 x64 使用 Nginx、Supervisor 部署 Go/Golang 服务

    前言 在 CentOS 9 x64 系统上,可以通过以下步骤来部署 Golang 服务. 1. 安装必要的软件包 安装以下软件包: Golang:Golang 编程语言 Nginx:Web 服务器 S ...

  3. 开放原子开源基金会OpenHarmony工作委员会主席侯培新寄语OpenAtom OpenHarmony分论坛

    2022开放原子全球开源峰会 OpenAtom OpenHarmony分论坛 万物互联,使能千行百业 7月27日 14:00  与您相约 OpenHarmony 工作委员会主席侯培新 寄语 OpenA ...

  4. Go 语言中 For 循环:语法、使用方法和实例教程

    for循环用于多次执行特定的代码块,每次都可以使用不同的值.每次循环执行都称为一次迭代.for循环可以包含最多三个语句: 语法 for 语句1; 语句2; 语句3 { // 每次迭代要执行的代码 } ...

  5. Git 13 暂存代码

    如果当前分支的功能还没开发完,无法提交代码. 但出现了紧急情况(比如线上BUG),需要立即切换到其他分支进行开发. 此时可以先暂存当前分支代码,等切换回当前分支的时候再恢复. 1.暂存当前分支变更: ...

  6. 探索“智”感生活,HMS Core线上Codelabs挑战赛第4期开始!

    HMS Core线上Codelabs挑战赛第4期正式开始!我们向所有实践力超强.创新力满满的开发者发出邀请,用你的超级"码"力,解锁更多应用价值! 生活里,我们被手机"秒 ...

  7. ASP.NET 部署常见问题及解决方案

    ASP.NET 部署部署过程中常见问题及解决方案 Could not load file or assembly 'XXXXX' or one of its dependencies. Access ...

  8. Access Single User Mode (Reset Root Password)--CentOS 修改root密码

    Access Single User Mode (Reset Root Password) Published on: Wed, Sep 17, 2014 at 12:52 pm EST FAQ  L ...

  9. ionic 4 app 自动版本更新

    前言 介绍一下ionic4 app的自动更新.ionic 不多介绍了,后面一个后系列,背负着骂名的ionic其实还是可以的,如果刚入门ionic可能觉得很坑,但是呢,往后你就发现另外一件事,那就是其他 ...

  10. leetcode:1337. 方阵中战斗力最弱的 K 行

    1337. 方阵中战斗力最弱的 K 行 给你一个大小为 m * n 的方阵 mat,方阵由若干军人和平民组成,分别用 0 和 1 表示. 请你返回方阵中战斗力最弱的 k 行的索引,按从最弱到最强排序. ...