想象一下我们有一个包含产品的 CRUD 系统。每个
Product
可以有代表产品属性的Attributes
,如尺寸、颜色、重量等。这是一个典型的EAV数据库案例(实体、属性、值)。
简单来说,数据库方案如下:
产品
id | name | description
属性
id | name | data_type
eav_关系
id | entity_id | attribute_id
小数值
id | eav_relation_id | value (column type decimal)
varchar_values
id | eav_relation_id | value (column type varchar)
..._值
Similar as the other values tables but for different data types, like bool_values, file_values, etc.
提供上下文:
表 eav_relations 保持产品及其属性之间的联系。 ..._values 表包含一个 eav_relation_id,它指向 eav_relations 中的一行。一个属性可以有多个值(例如“标签/关键字”的集合或多个文件)。
加载产品时,我们可以从数据库结构构建整个产品聚合,这是显而易见的。 但是当创建/更新产品时,客户端发送产品表示如下:
{
"name": "The product name",
"description": "Example product.",
"attribute_groups": [
{
"group_name": "Product propeties",
"attributes": [
{
"attribute_id": 3,
"values": [433.9]
},
{
"attribute_id": 9,
"values": ["wifi", "LAN"]
}
]
},
{
"name": "Shipping",
"attributes": [
{
"attribute_id": 19,
"values": [12.5]
},
{
"attribute_id": 24,
"values": ["ECommercePost Ltd."]
}
]
}
]
}
创建产品时,在数据库中创建关系数据相对简单。 但更新时就比较复杂了。属性可以被添加或删除,属性的值可以被更改/添加/删除。最简单的方法是删除并重新创建 eav_relations 中与产品对应的所有行,并删除并重新创建 ..._values 表中的所有行。重新创建时,自动增量将生成新的 id。缺点是行 id 在产品的每次更新时都会发生变化,而只要 id<->id 耦合保持正确,这在技术上并不是很重要。
否则,您必须在更新请求的 JSON 中提供所有 eav_relations 和 ..._values 的行 ID。然后检查具有这些 id 的行是否存在,如果存在,则更新它们。当存在“null”id 时,表示该属性是新添加到产品中的,如果属性值具有“null”id,则表示该值是新的。此外,在处理请求之前,您必须加载整个产品聚合,并检查数据库中是否有行的 id 在请求 JSON 中出现“不再”。在这种情况下,应删除该行(因为从产品中删除了属性或属性值)。 更新请求的 JSON 将变为如下所示:
{
"name": "The product name",
"description": "Example product.",
"attribute_groups": [
{
"group_name": "Product propeties",
"attributes": [
{
"eav_relation_id": 2, // Existing eav_relation id provided in request, the attribute is preserved.
"attribute_id": 3, // attribute_id is the same. If the attribute id would have been changed, the column will be updated in eav_relations. It's like the attribute would be swapped with some other...
"values": [
{
"id": 291, // Existing decimal_values row id is provided, the row will be kept but the value column will be updated.
"value": 123.9
}
]
},
{
"eav_relation_id": null, // A null id means the attribute is added.
"attribute_id": 98,
"values": [
{
"id": null,
"value": "orange"
},
{
"id": null,
"value": "blue"
}
]
}
// There was an attribute with id 9, (and values "wifi", "LAN") but this is no more present in the
// request. It's considered as dropped and thus the corresponding rows will be deleted.
]
},
// The group "Shipping" is no more present in the request, and so the eav_relation_ids do not occur in the request.
// All eav_relations and ..._values rows for the product, that have a row id that is no more present in the request
// will be deleted.
]
}
很明显,这带来了很大的复杂性,甚至令人难以理解。 我的想法是始终将产品行保留在products
表中,因此产品 ID 永远不会改变,因为这是实际上可以在 URL 中使用的重要标识符。其他“更深层次”的 id 实际上只是为了在内部保持数据之间的关系。 我的问题是,通常的做法是删除并重新创建整个聚合,还是我们应该更新并“回收”数据库行以避免自动增量并保留内部用于连接行的原始行 ID。
相反,您可以添加一个可以在
eav_relations
轻松复制产品和属性的唯一标识符,并且可以为
attributes
、decimal_values
和 varchar_values
创建临时表,在其中插入事物的当前状态以及一个临时的 eav_relations
,您还将在其中拥有这个新的唯一标识符(如果本质上是同一产品的相同属性,则它应该与实际表中的对应值匹配)。因此,您可以从 eav_relations
中删除并级联所有在临时对应项中不匹配的记录,执行更新和插入。此外,如果您已有十进制或 varchar 值,并且存储属性的值表已更改,那么您也需要转换和迁移属性值。