从零开始 Spring Boot 57:JPA中的一对多关系
图源:简书 (jianshu.com)
在上篇文章中我们介绍了如何在 JPA 中实现实体的一对一关系,在关系型数据库设计中,除了一对一关系,还存在一对多关系。本篇文章介绍如何在 JPA 中实现一对多关系。
模型
假设我们有两张表,学生表和电子邮件账号表,一个学生可以有多个电子邮件账号,一个电子邮件账号只能对应一个学生,这是典型的一对多关系,用数据库模型可以表示为:
实体
用 JPA 实体实现就是:
@Entity
@Table(name = "user_student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@NotBlank
@Length(max = 45)
private String name;
private List<Email> emails;
}
@Getter
@Entity
@Table(name = "user_email")
public class Email {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@NotBlank
@Length(max = 45)
private String account;
@NotNull
@NotBlank
@Length(max = 45)
private String domain;
private Student student;
public String toEmailAddress() {
return "%s@%s".formatted(this.getAccount(), this.getDomain());
}
}
Student
实体有一个emails
属性,表示一个学生可以拥有多个电子邮件。Email
实体有一个student
属性,表示一个电子邮件关联到一个学生。
关联关系
下面为这两个实体创建关联关系:
// ...
public class Student {
// ...
@OneToMany(mappedBy = "student",
fetch = FetchType.EAGER,
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Email> emails;
// ...
}
// ...
public class Email {
// ...
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "student_id")
private Student student;
// ...
}
方式类似于前一篇文章中介绍的一对一关系的实现,只不过是用@ManyToOne
取代@OneToOne
。想了解相关注解的详细说明的,可以阅读之前的文章。
值得注意的是,就像一对一关系的那样,一对多关系同样存在着“关系拥有者”和“非拥有者”的区别。显然,在一对多关系中,“关系拥有者”必然是一对多关系的“多”所对应的实体(Email
),因为这个实体对应的表(user_email
)才真正拥有指向“一”的主键的外键约束(@JoinColumn(name = "student_id")
)。
因此,在“关系拥有者”这边,我们需要用@JoinColumn
指定外键约束,在关系的另一端(Student
),我们需要用@OneToMany(mappedBy='student')
指定关系由Email.student
属性映射。
其实,“让一条信息能对应多条信息”并不一定要用两张表(实体)来实现,如果不需要对相应的信息进行检索和查询,完全可以在主表上用一个 JSON 格式的字段来保存多条“附加信息”。在 JPA 中可以通过属性转换器来实现,具体的方式可以阅读我的这篇文章。
The End,谢谢阅读。
可以从这里获取本文的完整示例代码。
参考资料
- 从零开始 Spring Boot 53:JPA 属性转换器 - 红茶的个人站点 (icexmoon.cn)
- 从零开始 Spring Boot 56:JPA中的一对一关系 - 红茶的个人站点 (icexmoon.cn)
- Hibernate One to Many Annotation Tutorial | Baeldung