写点什么

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

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







4. 关联问题



在领域模型中,关联表示了实体之间的关系。面向对象的语言如Java使用对象引用来表示关联,而在关系模型中,外键约束列表示了一个关联,它带有一些键值的副本。



4.1 关联的方向性



关联是有方向性的,通过对象引用的方式从一个实体(或值对象)指向另一个实体。它们都是指针。





Java代码中,体现为一个实体类拥有一个类型为另一个实体类(或其集合)的属性:



public class Account {
private Customer owner;
...
}
public class Customer {
...
}



如果要实现双向关联,需要在两端分别定义关联:





关系模型采用外键的方式记录表之间的关联关系。在特定的方向上导航对于关系模型来说没有意义,因为可以使用join和投影操作创建任意的数据关联。其挑战在于将一个完全开放、独立于数据使用应用程序的数据模型影射到一个依赖应用程序的导航模型——这个特定应用程序所需的关联约束视图。



4.2 多对多关联



在领域模型中常常存在多对多关联。例如在选课系统中,一个学生可以选择多门课程,一门课程也可以被多个学生选中。





Java代码如下:



public class Student {
private Set<Course> selectedCourses;
...
}
public class Course {
private Set<Student> selectedBy;
...
}



而关系模型中,只能通过外键表示“多对一”或者"一对多"关联,没有对“多对多”关联的直接支持。



5. 数据导航问题



Java中,可以跟随关联的方向遍历对象网络,例如:



aUser.getBillingDetails().iterator().next();



但是这样的访问会导致著名的n + 1问题:需要为每一个访问节点或者对象网络的集合执行一条语句,导致性能低下。



在数据库中,可以根据外键关联用join的方式一次性获取数据,因而可以大大提高性能:



select * from USERS u
left outer join BILLING_DETAILS bd on bd.USER_ID = u.ID
where u.ID = 123;



6. 值对象的生命周期问题



前文说过,实体的状态分为“值”和“关联”两种,值包括简单值(数字、字符串、布尔值、日期等等)和值对象(EmailMoneyAddress等等)。值位于实体的边界之内,是实体的内在组成部分,而关联指向一个外部实体,不是实体的内在组成部分。因此,作为实体属性的简单值和值对象,不论是单值的还是多值的(数组、列表或集合等形式),都是实体的固有组成部分,从属于实体,不具备独立的生命周期。它们会随着实体创建而创建,随着实体的更新而更新,也会随着实体的删除而删除。不需要任何额外的步骤去处理值的生命周期。



举个例子,在电商软件中有三个领域类,包括一个实体类Customer和两个值对象AddressCustomerNameCustomer类拥有一个类型为PersonName的单值属性name以及一个类型为Address的多值属性shippingAddresses(一个顾客拥有一个名称和多个送货地址)。下面是Java代码:



public class Customer {
private long id;
private CustomerName name;
private Set<Address> shippingAddresses = new HashSet();
...
//getters and setters
}
public class CustomerName {
private String firstName;
private String lastName;
...
//getters and setters
}
public class Address {
private String postalCode;
private String province;
private String city;
private String street;
...
//getters and setters
}



根据领域模型的含义,值对象PersonNameAddress都不需要单独持久化,体现在:



  • 当保存新建的Customer实体对象时,单值属性name持有的值对象CustomerName和多值属性shippingAddresses指向的值对象Address的集合都会自动保存,而不需要分别保存CustomerCustomerNameAddress

  • 当对shippingAddresses这个集合增减条目或修改其中部分地址信息时,只须用新的地址集合整体替换掉现有的地址集合,不用担心其中哪些是新的,哪些是旧的;既不必担心造成重复,也不必担心出现“孤儿对象”——在这里指不关联到任何PersonAddress实例。

  • 当删除Person实体对象时,它持有的CustomerName实例和Address集合会一并删除。



而关系模型没有实体和值对象之别。它只认知两个表之间通过外键存在的关联关系。必须分别管理关联的两端的表,而不能做到级联持久化或者级联删除。



详细内容请戳这里↓↓↓



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



这一节就讲到这里,下一节我们讲"JPA,Hibernate与Spring Data JPA"



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





发布于: 2020 年 09 月 29 日阅读数: 43
用户头像

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

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

评论

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