写点什么

原创 | 使用 JPA 实现 DDD 持久化 -O/R 阻抗失配 (1/2)

发布于: 2020 年 08 月 26 日
原创 | 使用JPA实现DDD持久化-O/R阻抗失配(1/2)







在使用面向对象的编程语言编写的应用程序中,对象是其基础构建块。由于前文表述的种种原因,我们需要将领域对象的状态持久化到某种存储媒体中,以便在需要时能够重建这些对象。通常而言,是将领域对象状态(值和关联)持久化到关系数据库中。



在以关系数据库为目标的持久化中,我们会尽量:



  • 将领域对象中的实体类与数据库中的表一一对应

  • 将实体实例与表中的记录(行)一一对应

  • 将实体实例属性与表中的字段(列)一一对应

  • 将实体类的标识属性对应到表中的主键字段



但是,对象模型(O)和关系模型(R)是基于不同的理论,针对不同的目标建立的,两者之间并不能那么完美匹配。实际上,两者在很多方面存在“阻抗失配(Impedance Mismatch)”。主要体现在以下的方面。



1. 粒度问题



我们知道,在领域模型中,有些实体持有值对象,这些值对象本身可能有多个属性。有时候,值对象内部甚至还包含下一级的值对象。类的粒度从粗到细:实体-值对象-简单属性,可以有3个甚至3个以上的粒度层次。



例如在电商网站中,顾客类Customer持有配送地址shippingAddress这样一个Address类型的值对象属性。





而关系数据库中只有两种粒度:由你创建的关系类型(表),以及内置的数据类型,如VARCHARBIGINTTIMESTAMP等。它不能直接对应到对象模型中的多级粒度。尽管各种数据库系统往往允许用户创建自定义类型,但是操作起来很复杂,更关键的是:不同数据库系统的自定义数据类型不可移植。



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部分"



如果觉得有收获,点个【赞】鼓励一下呗!



发布于: 2020 年 08 月 26 日阅读数: 92
用户头像

高级架构师,技术顾问,交流公号:编程道与术 2020.04.28 加入

杨宇于2020年创立编程道与术,致力于研究领域分析与建模、测试驱动开发、架构设计、自动化构建和持续集成、敏捷开发方法论、微服务、云计算等顶尖技术领域。 了解更多公众号:编程道与术

评论

发布
暂无评论
原创 | 使用JPA实现DDD持久化-O/R阻抗失配(1/2)