我们如何处理数据库中的多态性?
例子
我有Person
,SpecialPerson
和User
。 "人员"和"特殊人员"只是他们在网站上没有用户名或者密码的人,但它们存储在数据库中以备记录。用户拥有与"个人"和可能的"特殊个人"相同的所有数据,以及在网站上注册时的用户名和密码。
我们将如何解决这个问题?我们是否有一个"人"表,该表存储一个人共有的所有数据,并使用密钥在"特殊人"(如果是特殊人)和"用户"(如果是用户)中查找其数据,反之亦然?
解决方案
回答
就个人而言,我会将所有这些不同的用户类存储在一个表中。然后,我们可以具有一个存储"类型"值的字段,也可以隐含要填充的字段来暗示我们正在处理的是哪种类型的人。例如,如果UserID为NULL,则此记录不是用户。
我们可以使用一对一或者无连接类型链接到其他表,但是在每个查询中,我们将添加额外的连接。
如果我们决定沿该路线走,则LINQ-to-SQL也支持第一种方法(他们称其为"每个层次的表"或者" TPH")。
回答
过去,我完全按照建议进行了此操作-拥有一个用于常见内容的Person表,然后为派生类链接了SpecialPerson。但是,我重新考虑一下,因为Linq2Sql希望在同一表中有一个字段来指示差异。我没有过多地研究实体模型,尽管-可以肯定,这允许使用另一种方法。
回答
是的,如果可能会有更多类型,我还将考虑使用TypeID和PersonType表。但是,如果只有3个不应该是nec。
回答
如果"用户","人"和"特殊人"都具有相同的外键,那么我将只有一个表。添加一列"类型",该列必须限制为"用户","人"或者"特殊人"。然后根据Type的值对其他可选列进行约束。
对于目标代码,如果我们具有单独的表或者多个表来表示多态性,则没有太大的区别。但是,如果我们必须对数据库执行SQL,则如果将多态性捕获在单个表中,则要容易得多...只要子类型的外键相同。
回答
我会说,根据"人格"和"特殊人格"的区别,我们可能不希望该任务具有多态性。
我将创建一个User表,一个Person表,该表具有User的可为空的外键字段(即Person可以是User,但不一定是)。
然后,我将创建一个与Person表相关的SpecialPerson表,其中包含任何其他字段。如果在SpecialPerson中有给定Person.ID的记录,则他/她/它是一个特殊的人。
回答
通常,有三种将对象继承映射到数据库表的方式。
我们可以用来自所有对象的所有字段组成一个大表,并为该类型添加一个特殊字段。尽管现代数据库通过不存储空字段来节省空间,但速度很快,但是却浪费了空间。而且,如果我们仅查找表中的所有用户,那么其中的每种类型的人都会变得很慢。并非所有or-mapper都支持此功能。
我们可以为所有不同的子类创建不同的表,并且所有表都包含基类字段。从性能的角度来看,这是可以的。但是从维护的角度来看并非如此。每次基类更改时,所有表都会更改。
我们也可以像建议的那样为每个班级制作一张表格。这样,我们需要进行联接才能获取所有数据。因此它的性能较差。我认为这是最干净的解决方案。
我们要使用的内容当然取决于情况。这些解决方案都不是完美的,因此我们必须权衡利弊。
回答
我在这里要说的是使数据库架构师陷入困境,但这是这样的:
将数据库视图视为等效于接口定义。
一个表相当于一个类。
因此,在示例中,所有3个人类都将实现IPerson接口。
因此,我们有3个表,每个表分别用于"用户","人"和"特殊人"。
然后有一个" PersonView"视图或者从所有3个表中选择通用属性(由"接口"定义的东西)进入单个视图。
在此视图中使用" PersonType"列来存储要存储的人员的实际类型。
因此,当我们运行可以对任何类型的人进行操作的查询时,只需查询PersonView视图即可。
回答
在关系数据库中,有三种处理继承的基本策略,根据实际需要,还有许多更复杂/定制的替代方案。
- 每个类层次结构的表。一张表代表整个层次结构。
- 每个子类的表。将为每个子类创建一个单独的表,并且子类表之间的关联为0-1.
- 每个具体类的表。为每个具体类创建一个表。
这些方法中的每一个都会产生有关规范化,数据访问代码和数据存储的问题,尽管我个人的偏爱是按子类使用表,除非有特定的性能或者结构上的原因要使用其他选择之一。
回答
冒着成为"建筑宇航员"的风险,我更倾向于为子类使用单独的表格。子类表的主键也应该是链接回超类型的外键。
这样做的主要原因是它在逻辑上变得更加一致,并且我们最终不会获得很多对于该特定记录而言为NULL且毫无意义的字段。这种方法还使在迭代设计过程中向子类型添加额外字段变得更加容易。
这的确增加了向查询中添加JOIN的缺点,这可能会影响性能,但是我几乎总是总是先采用理想的设计,然后在证明有必要的情况下再进行优化。几次我首先采用"最佳"方式,后来我几乎总是后悔。
所以我的设计就像
人(人格,姓名,地址,电话等)
SPECIALPERSON(personid参考人(personid),其他字段...)
USER(个人身份参考人员(个人身份),用户名,加密密码,其他字段...)
如果需要的话,我们也可以稍后在其上创建聚合父类型和子类型的VIEW。
这种方法的一个缺陷是,如果我们发现自己正在大量搜索与特定超类型相关联的子类型。对此,没有一个简单的答案,我们可以在需要时以编程方式对其进行跟踪,或者运行soem全局查询并缓存结果。这实际上取决于应用程序。
回答
这可能不是OP想要问的,但是我想我可以把它扔在这里。
我最近在一个项目中有一个独特的db多态现象。我们有60到120个可能的类,每个类都有自己的30到40个唯一属性集,所有类上约有10 12个共同属性。我们决定采用SQL-XML路由,最后只有一个表。就像是 :
PERSON (personid,persontype, name,address, phone, XMLOtherProperties)
包含所有常用属性作为列,然后包含一个大型XML属性包。然后,ORM层负责从XMLOtherProperties中读取/写入相应的属性。有一点像 :
public string StrangeProperty { get { return XMLPropertyBag["StrangeProperty"];} set { XMLPropertyBag["StrangeProperty"]= value;} }
(我们最终将xml列映射为Hastable而不是XML doc,但是我们可以使用最适合DAL的任何方式)
它不会赢得任何设计大奖,但是如果我们有很多(或者未知)可能的类,它将可以使用。在SQL2005中,我们仍然可以在SQL查询中使用XPATH基于存储为XML的某些属性来选择行。
回答
看看Martin Fowler的企业应用程序架构模式:
- 单表继承:
When mapping to a relational database, we try to minimize the joins that can quickly mount up when processing an inheritance structure in multiple tables. Single Table Inheritance maps all fields of all classes of an inheritance structure into a single table.
- 类表继承:
You want database structures that map clearly to the objects and allow links anywhere in the inheritance structure. Class Table Inheritance supports this by using one database table per class in the inheritance structure.
- 具体表继承:
Thinking of tables from an object instance point of view, a sensible route is to take each object in memory and map it to a single database row. This implies Concrete Table Inheritance, where there's a table for each concrete class in the inheritance hierarchy.