关系数据库中面向对象的结构

时间:2009-03-01 22:37:58

标签: php mysql database oop

伙计,

连续第n次,我再次遇到同样的老问题。它是关于“如何以无痛的方式将OOP结构映射到数据库表。”

这是一个场景:我的系统中有几种类型的“演员” - 工人,雇主,联系人。它们具有某些共同的功能;其他作品则截然不同。所有参与者处理的实体都是“通信”,“笔记”(管理员喜欢给客户留言)等等。每种演员类型处理的其他实体有很多类型,而其他实体则没有。

目前,我的数据库架构包括以下表格:

行动者:

  • 工人
  • 雇主
  • 接触

实体:

  • 通信
  • 注释

实体和参与者之间的关联表:

  • 工人通信ASSN
  • 雇主通信ASSN
  • 工人笔记-ASSN
  • 等,你得到了演练。

这对我来说就像是“代码味道”。每当客户改变他们的角色(即从“联系人”升级到“雇主”)时,就需要运行一堆疯狂的脚本。 Yuck ...另一方面,如果我在一个纯粹由OOP驱动的世界中运行,这将更加容易 - 为所有具有共同属性的实体提供基类,并完成它...

在DB世界中,这个选项似乎在理论上是可行的,但听起来非常混乱...... I.e。如果我理解这一点,我会有一个新的base_actor表,并且每个其他actor都有一个base_actor_id,然后关联将在base_actor和实体之间......但是,我如何进行反向关联查询?即“告诉我所有与工人类型的演员的沟通”?

有什么建议吗?关于“将OOP结构映射到关系数据库”主题的一般想法?

11 个答案:

答案 0 :(得分:8)

  

..这是关于“如何以无痛的方式将OOP结构映射到数据库表。”

你没有。

面向对象和关系代数是两种根本不同的范例。没有主观解释,你不能在它们之间转换。这被称为阻抗不匹配,并被称为The Vietnam of Computer Science

答案 1 :(得分:7)

它被称为ORM or Object Relational Mapping。有许多产品旨在帮助您将OO结构映射到关系表。例如,Ruby on Rails提供Active Record以帮助弥合鸿沟。对于PHP,您有PropelDoctrine以及Porte和其他许多人。

答案 2 :(得分:6)

这是我10年前提出的解决方案。使用此设计的系统仍在运行,因此它的运行时间比我的大多数代码都要长。 ;)今天我可以使用Scott提到的一个ORM包,但是直接使用SQL确实没有大问题。

  1. 将所有继承关系建模为表之间的连接。系统中的每个表都将包含特定类的属性。

  2. 使用合成对象id(oid)作为所有对象的主键。序列生成器或自动增量列是生成oid值所必需的。

  3. 所有继承的类必须使用与其父类相同的oid类型。将oid定义为具有级联删除的外键。父表获取自动增量oid列,子项获取普通的oid列。

  4. 最终课程的查询在相应的表格上进行。您可以将所有父类表连接到查询中,也可以延迟加载所需的属性。如果您的继承层次结构很深并且您有许多类,那么ORM包可以真正简化您的代码。我的系统少于50个类,最大继承深度为3。

  5. 跨子类的查询(即父类的查询)可以基于每个实例延迟加载子属性,也可以对与基类连接的每个子类重复查询。基于父类查询的延迟加载子属性要求您知道对象的类型。您可能已经在父类中有足够的信息,但如果没有,则需要添加类型信息。同样,这是ORM包可以提供帮助的地方。

  6. 可以在表结构中跳过没有成员属性的虚拟类,但是您将无法根据这些类进行查询。

    这就是“向我展示与工人类型的演员的所有沟通”的样子。

    select * from comm c, worker w where c.actor=w.oid;
    

    如果你有通信的子类,并且想要立即加载所有子类属性(可能你的系统不允许部分构造),最简单的解决方案就是热切加入所有可能的类。

    select * from comm c, worker w, missive m where c.actor=w.oid and c.oid=m.oid;
    select * from comm c, worker w, shoutout s where c.actor=w.oid and c.oid=s.oid;
    

    最后一件事。确保您拥有良好的数据库和正确的索引。如果数据库无法优化这些连接,则性能可能是一个严重的问题。

答案 3 :(得分:3)

你提出的建议似乎对我来说是合理的。您可以将actortype列添加到actor-base表,以区分不同类型的actor。每个特定actor表的PK都是actorbase表的FK,以避免“毛茸茸”的查询并模拟类似继承的“is-a”关系。

答案 4 :(得分:3)

你在寻找的是Disjoint-subtypes ...... ORM是一个黑客。

答案 5 :(得分:2)

我见过的最佳答案是: http://en.wikipedia.org/wiki/The_Third_Manifesto

不幸的是,这不适合stackoverflow中单个答案的空间。我将在这里尝试缩写,但我警告你,这样的缩写不会准确反映第三个宣言。请重新引导对此解决方案的所有批评实际上阅读该死的东西,而不是假设您通过阅读缩写完全理解它。好的,就在这里。

定义三个名为worker,employer和contact的新列类型。将这些类型中的每一种的对象存储在各自类型的列中。遵循标准化规则的其余数据模型。

我的感觉是,当前流行的数据库技术实际上并不支持“正确”的方式来做这些事情(具体来说,许多数据库系统不允许定义新类型)。所以无论你做什么都没关系,你总会被迫陷入妥协的境地。但在阅读完第三份宣言后,至少你会知道你在做什么妥协。

目前,ORM目前是解决该问题的绝大多数流行解决方案,但我认为这不是正确的解决方案。

答案 6 :(得分:1)

this question中所述,您可能值得熟悉对象角色建模。我看到的最大问题是,没有现成的方法可以对关系数据进行概念设计讨论。您可以做的最好的是逻辑建模(通常是ERM)。对象角色建模为该讨论提供了基础。我希望您能从类似的OOP设计讨论中看到可识别的工件。

答案 7 :(得分:0)

对我来说,看起来您的数据模型缺少一个级别。我会把它设置得更像这样:

表 - (只是关于实际人员的信息)

角色表 - (人们可以拥有的角色类型,即工人,雇主,联系人 - 以及特定于该角色的信息)

PeopleRoles 表 - (people_id,role_id,可能会启动/修改日期等)。

实体表 - (定义不同类型的实体)

RoleEntities 表 - (role_id,entity_id等)

然后将Person从一个角色更改为另一个角色(或允许他们拥有多个角色)是一个简单的更新。

答案 8 :(得分:0)

许多RDBMS提供了表继承功能,它将父表链接到子表,其方式与类继承非常相似。从供应商到供应商的实施方式略有不同,但实施类似概念可能会带来一些麻烦。

此外,大多数RDBMS都有一些触发器,存储视图和存储过程的组合,可以将行为与实现分开。在许多情况下,例如PostgreSQL的规则(视图的泛化)提供了非常复杂的封装并且非常易于使用。

答案 9 :(得分:0)

有几个人注意到物体 - 关系阻抗不匹配。最好的解决方案是简单地放弃RDBMS支持OODBMS,而this result最近已经重新受欢迎。

也就是说,据我所知,在纯PHP中没有任何带有API的对象数据库。快速搜索生成Hibernate,但多年来一直没有更新。另一方面,我听说过很多其他语言的对象数据库,包括db4oZODB和{{3}}。

答案 10 :(得分:0)

我建议您使用LINQ for PHP。我知道.Net LINQ,但我猜PHPLinq值得一试。

http://phplinq.codeplex.com/