从零开始 Spring Boot 56:JPA中的一对一关系
在对数据库进行建模的时候,有些表之间是存在关联关系的,这种关联关系分为多种:
- 一对一
- 一对多
- 多对多
这篇文章将介绍如何在 JPA(Hibernate)中实现一对一关系。
其余的关联关系会在之后介绍。
通过外键关联
即使都是一对一的关系,也会因为你建模的不同而有不同的实现方式,这里先介绍最常见的——两个表之间通过外键进行关联。
假设我们的项目中需要两张表,学生和附加的一些额外学生信息,两张表的关系可以用以下模型表示:
student
表保存基本信息,student_info
表保存一些额外的附加信息。所以两张表的数据是一对一的关系,这体现在student
表中用外键student_info_id
关联student_info
表中的主键id
。
- 表设计中通常会将表按照模块划分,并使用前缀命名的方式进行区分。所以这里的
user_
表示两张表属于user
模块。- 之所以将一张表就可以表示的信息进行拆分,可能的因素有很多,其中一个可能的原因是性能优化需要,通过将频繁会发生变化的字段从基本表中拆出,可以缩小数据变更会影响的数据规模,进而提升性能。
下面我们看怎么在 JPA 中实现这个模型。
先实现表对应的两个实体类:
@Entity
@Table(name = "user_student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Length(max = 45)
private String name;
@NotNull
private LocalDate birthDay;
private Long studentInfoId;
}
@Entity
@Table(name = "user_student_info")
public class StudentInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
private Boolean loveMusic;
@NotNull
private Boolean loveDraw;
@NotNull
@Length(max = 255)
@Column(name = "description")
private String desc;
}
这里
desc
属性没有使用属性名称作为 DDL 的字段名,是因为在 MySQL 中desc
是一个保留字。
下面实现实体中的一对一映射关系:
public class Student {
// ...
@OneToOne
@JoinColumn(name = "student_info_id", referencedColumnName = "id")
private StudentInfo studentInfo;
}
一对一的关系可以是双向关联,也可以是单向关联。在这个示例中仅是单向关联,所以只要在Student
中说明关联关系,而无需修改StudentInfo
实体。
因为一个实体对应一张表,一个实体实例对应表中的一行数据,所以很自然的,当前的Student
中会唯一对应一个StudentInfo
,因此这里用StudentInfo
属性取代了原始代表外键的studentInfoId
属性。
此外,@OneToOne
说明了这是一个一对一关联,@JoinColumn
则指明了关联关系是由当前表的name
字段(student_info_id)指向被关联表的referencedColumnName
字段(id)。
@JoinColumn
的referencedColumnName
属性可以缺省,此时 Hibernate 会使用被关联实体的主键对应的表字段。
最终,Hibernate 自动生成的 DDL 如下:
CREATE TABLE `user_student` (
`id` bigint NOT NULL AUTO_INCREMENT,
`birth_day` date NOT NULL,
`name` varchar(45) NOT NULL,
`student_info_id` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_5hyl4mptebabeuk4gfh59jwql` (`student_info_id`),
CONSTRAINT `FK24w6cbvyfnc718wueavv7355l` FOREIGN KEY (`student_info_id`) REFERENCES `user_student_info` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `user_student_info` (
`id` bigint NOT NULL AUTO_INCREMENT,
`description` varchar(255) NOT NULL,
`love_draw` bit(1) NOT NULL,
`love_music` bit(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE&