原创 | 使用 JPA 实现 DDD 持久化 -O/R 阻抗失配 (1/2)
在使用面向对象的编程语言编写的应用程序中,对象是其基础构建块。由于前文表述的种种原因,我们需要将领域对象的状态持久化到某种存储媒体中,以便在需要时能够重建这些对象。通常而言,是将领域对象状态(值和关联)持久化到关系数据库中。
在以关系数据库为目标的持久化中,我们会尽量:
将领域对象中的实体类与数据库中的表一一对应
将实体实例与表中的记录(行)一一对应
将实体实例属性与表中的字段(列)一一对应
将实体类的标识属性对应到表中的主键字段
但是,对象模型(O
)和关系模型(R
)是基于不同的理论,针对不同的目标建立的,两者之间并不能那么完美匹配。实际上,两者在很多方面存在“阻抗失配(Impedance Mismatch
)”。主要体现在以下的方面。
1. 粒度问题
我们知道,在领域模型中,有些实体持有值对象,这些值对象本身可能有多个属性。有时候,值对象内部甚至还包含下一级的值对象。类的粒度从粗到细:实体-值对象-简单属性,可以有3个甚至3个以上的粒度层次。
例如在电商网站中,顾客类Customer
持有配送地址shippingAddress
这样一个Address
类型的值对象属性。
而关系数据库中只有两种粒度:由你创建的关系类型(表),以及内置的数据类型,如VARCHAR
、BIGINT
和TIMESTAMP
等。它不能直接对应到对象模型中的多级粒度。尽管各种数据库系统往往允许用户创建自定义类型,但是操作起来很复杂,更关键的是:不同数据库系统的自定义数据类型不可移植。
2. 标识问题
在应用程序和数据库中,常常需要判断两个对象或者两行数据是不是相同的(代表相同的对象或数据行),就是通过什么样的标识来识别两个事物是不是相同的事物。
在Java
中,有两种不同的相等性概念:
实例标识:大致等同于对象的内存位置,使用
a == b
进行检查。实例等价性:通过
equals()
方法的实现进行判断。
另一方面,数据行的标识会体现为主键值的比较。同一个表中主键值相同两个数据行代表同一行数据。
Java
中有几个不相等的对象实例同时对应数据库同一行的情况很常见,例如在并发的应用程序线程中就是如此。
3. 继承与多态问题
面向对象有三大特征:封装、继承和多态。Java
作为面向对象的编程语言,可以使用基类和子类来实现类型继承。在领域模型中,类型继承是非常常见的。例如银行系统的领域模型:
银行账户Account
这个超类有储蓄账户SavingsAccount
和信用账户CreditAccount
两种子类型。在超类Account
中可以定义所有子类的共同属性(锁定状态locked
、当前余额balance
等),同时每个子类还可以分别定义自己特有的属性(例如信用账户CreditAccount
子类拥有一个特有属性:信用额度creditLimit
)。
Account
抽象超类有一个owner
(账户持有人)属性,指向另一个抽象基类Customer
(客户)。Customer
有两个具体子类:公司客户CompanyCustomer
和个人客户PersonalCustomer
。两个子类分别拥有自己的一批专有属性。
子类除了可以添加自己特有的属性之外,还可以添加方法、重写或实现超类的方法。例如超类Account
上定义了抽象方法credit()
,两个子类分别为这个方法提供自己的实现。
基于上面的分析,持久化必须:
允许进行多态关联。
允许进行多态查询。
关系数据库中没有任何与数据继承和多态相关的概念。表之间没有继承关系,SQL
也不提供任何多态查询的功能。
详细内容请戳这里↓↓↓
原创 | 使用JPA实现DDD持久化-O/R阻抗失配(1/2)
这一节就讲到这里,下一节我们讲"O/R阻抗失配第2部分"。
如果觉得有收获,点个【赞】鼓励一下呗!
版权声明: 本文为 InfoQ 作者【编程道与术】的原创文章。
原文链接:【http://xie.infoq.cn/article/7b786e793670cdab5b4ffc49c】。文章转载请联系作者。
评论