4. 关联问题
在领域模型中,关联表示了实体之间的关系。面向对象的语言如Java
使用对象引用来表示关联,而在关系模型中,外键约束列表示了一个关联,它带有一些键值的副本。
4.1 关联的方向性
关联是有方向性的,通过对象引用的方式从一个实体(或值对象)指向另一个实体。它们都是指针。
在Java
代码中,体现为一个实体类拥有一个类型为另一个实体类(或其集合)的属性:
public class Account {
private Customer owner;
...
}
public class Customer {
...
}
如果要实现双向关联,需要在两端分别定义关联:
关系模型采用外键的方式记录表之间的关联关系。在特定的方向上导航对于关系模型来说没有意义,因为可以使用join
和投影操作创建任意的数据关联。其挑战在于将一个完全开放、独立于数据使用应用程序的数据模型影射到一个依赖应用程序的导航模型——这个特定应用程序所需的关联约束视图。
4.2 多对多关联
在领域模型中常常存在多对多关联。例如在选课系统中,一个学生可以选择多门课程,一门课程也可以被多个学生选中。
而关系模型中,只能通过外键表示“多对一”或者"一对多"关联,没有对“多对多”关联的直接支持。
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. 值对象的生命周期问题
前文说过,实体的状态分为“值”和“关联”两种,值包括简单值(数字、字符串、布尔值、日期等等)和值对象(Email
、Money
、Address
等等)。值位于实体的边界之内,是实体的内在组成部分,而关联指向一个外部实体,不是实体的内在组成部分。因此,作为实体属性的简单值和值对象,不论是单值的还是多值的(数组、列表或集合等形式),都是实体的固有组成部分,从属于实体,不具备独立的生命周期。它们会随着实体创建而创建,随着实体的更新而更新,也会随着实体的删除而删除。不需要任何额外的步骤去处理值的生命周期。
举个例子,在电商软件中有三个领域类,包括一个实体类Customer
和两个值对象Address
和CustomerName
。Customer
类拥有一个类型为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
}
根据领域模型的含义,值对象PersonName
和Address
都不需要单独持久化,体现在:
- 当保存新建的
Customer
实体对象时,单值属性name
持有的值对象CustomerName
和多值属性shippingAddresses
指向的值对象Address
的集合都会自动保存,而不需要分别保存Customer
、CustomerName
和Address
。 - 当对
shippingAddresses
这个集合增减条目或修改其中部分地址信息时,只须用新的地址集合整体替换掉现有的地址集合,不用担心其中哪些是新的,哪些是旧的;既不必担心造成重复,也不必担心出现“孤儿对象”——在这里指不关联到任何Person
的Address
实例。 - 当删除
Person
实体对象时,它持有的CustomerName
实例和Address
集合会一并删除。
而关系模型没有实体和值对象之别。它只认知两个表之间通过外键存在的关联关系。必须分别管理关联的两端的表,而不能做到级联持久化或者级联删除。
详细内容请戳这里↓↓↓
原创 | 使用JPA实现DDD持久化-O/R阻抗失配(2/2)
这一节就讲到这里,下一节我们讲"JPA,Hibernate与Spring Data JPA"。
如果觉得有收获,点个【赞】鼓励一下呗!