【发布时间】:2018-10-22 20:13:32
【问题描述】:
更新: 以下模式只是需要与其他模块具有多态关系的一个模块 (cmets) 的简化示例。该应用程序有许多模块(30+),用户可以通过管理员添加新模块。因此,每个模块都可以“链接”到另一个模块,但在编码时我们不知道模块名称和表名称。因此我们需要一个多态关系。
我正在尝试使用多态关联执行与动态表名的联接。
假设以下 MySQL 表
modules table
module_id | name | table_name
1 | Quote | quotes
2 | Order | orders
3 | Product | products
注意:系统允许通过管理员添加新模块,因此我们在编码时不知道表名和列名。现有模块的模块名称或 table_name 也可以随时间变化。
quotes table
id | name
22 | "Quote #Q-22"
23 | "Quote #Q-23"
orders table
id | name
122 | "Order #O-122"
123 | "Order #O-123"
products table
id | name
55 | "Product #P-55"
56 | "Product #P-56"
comments table
id | module_id | record_id | text
1 | 1 | 23 | hello
2 | 2 | 122 | big
3 | 3 | 55 | world
我想运行一个返回以下行的查询:
comments
id | name | text
1 | Quote #Q-23 | hello
2 | Order #O-122 | big
3 | Product #P-55 | world
Laravel 允许 polymorphic relationship 通过将 ORM 类名或表名直接存储在父表上。
这留给 ORM 来处理这种关系,据我了解,这通常会带来一些性能代价。 我认为性能会受到明显影响的原因是:
- 雄辩的开销。对于 1000 行,它将为每一行创建一个雄辩的对象。我喜欢 Eloquent,但在检索大量行时,它的性能远不理想。 Query Builder 更适合这种情况,但不支持多态性。
- 由于关联是在 Eloquent 级别完成的,我相信它需要额外的查询。我还没有对此进行测试,否则,Eloquent 将如何知道要加入哪些表?我很高兴在这个问题上犯了错误。
- 有时还会导致其他维护问题,例如更新表名或更改 ORM 类名/命名空间(尽管有解决方案)。
- 这绕过了外键约束。
我一般不喜欢将表名或模型直接与记录表耦合的想法。 这种方法的另一个重要(至少对我而言)缺点是它将数据库模式与应用程序紧密耦合。或者说,依赖于雄辩的“魔力”。如果我们在某个时候想要使用 Node.js、Python 或 GoLang(我们目前正在实现微服务架构),我们可能无法使用它,并且必须重新构建代码和数据库。
我读过一些其他文章和问题,例如 this one 或 this one。
我想到的一种方法是使用 MySQL 函数或存储过程来动态获取表名并连接结果。我能够使用存储过程和函数来动态获取表名,但似乎不可能加入存储过程的结果。函数似乎也不能解决这个问题。
第二种方法是将记录名称保存在单独的表中。这不适用于获取整条记录(报价单、订单等),但由于我只需要记录名称,因此它可以正常工作(当然需要适当的索引)。
record_name table
module_id | record_id | name
1 | 23 | Quote #Q-23
2 | 122 | Order #O-122
3 | 55 | Product #P-55
这种方法有一些缺点 - 首先,它仅适用于获取选定的键(在本例中为名称)。第二是它复制数据并违反其他数据库原则。最后,更新 module_name 表需要维护。
因此,我的问题如下 -
- 是否有很好的替代方法可以在数据库级别 (MySQL) 对这种关系进行建模,或者这必须在应用程序级别完成?
- MySQL 存储过程或函数可以解决这个问题吗?
【问题讨论】:
标签: mysql laravel polymorphic-associations