实体类的关系
我们已经了解了如何定义实体类和Repository:
•实体类中的标注,就是描述实体类到数据库Schema的映射,对应于DDL操作
•Repository接口中的方法,则对应于DML操作,即对数据库的增删改查
此前看到的标注都是关于Schema自身字段的信息,Schema之间还存在关系,比如外键关联。相应地,JPA@OneToOne、@ManyToOne、OneToMany和@ManyToMany几个标注提供用以定义实体类之间的关系。
首先来了解一下@OneToOne,相关概念梳理清楚了,后面几个标注也很容易理解。
基础使用
一个博客站点一般会有博客的名称、简介和背景图片等信息,这里通过BlogMetaInfo类来表示。关于(About)页面通常也需要使用这些信息,将其保存在数据库中可以进行动态配置。
那么User类和BlogMetaInfo类显然是一对一的关系,实体类的定义中需要使用@OneToOne标注。
1 |
|
这里BlogMetaInfo
包含了一个User类型的属性,并且通过@OneToOne
标注。
@OneToOne的属性
- targetEntity属性表示默认关联的实体类型,默认为当前标注的实体类,绝大数据情况下不需要进行设置。
-cascade属性表示级联操作策略:- 不定义,则对关系表不会产生任何影响
- CascadeType.PERSIST:级联新建
- CascadeType.REMOVE:级联删除
- CascadeType.REFRESH: 级联刷新,即重新同步到数据库中状态,会覆盖掉已经修改但是还没保存的实体类属性
- CascadeType.MERGE: 级联更新
- CascadeType.ALL:表示选择全部四项
- fetch属性表示实体的加载方式,有LAZY和EAGER两种取值,默认值为EAGER
- ptional属性表示关联的实体是否能够存在null值,默认为true,表示可以存在null值
关于fetch属性,所有@XXXToOne,默认值为EAGER,所有@XXXToMany,默认值为LAZY。
@JoinColumn 的使用
前一节示例代码中的@OneToOne标注,对应于BlogMetaInfo表中的User_Id字段,自动映射的字段名按照如下方式命名:关联表的名称 + "" + 关联表主键的字段名
这就是User_Id的由来。如果对应的字段名是其他名字,则可以通过@JoinColumn来定义外键关联的字段名称。比如如果字段名为blogUser_Id的话,可以这样定义:
1 |
|
默认情况下,关联实体(User)的主键一般用来做外键。如果不使用主键作为外键,则需要设置referencedColumnName
属性,如:
@JoinColumn(name=”blogUser_Id”, referencedColumnName=”other_column_for_fk”)
@JoinColumn的其它属性与@Column是类似的,可以参考前一节的内容。
unique属性
注意在我们的代码中,由于是基于实体类定义自动生成Schema,需要增加@JoinColumn(unique = true)
才会在BlogMetaInfo将user_id
字段设置为unique。实际开发中,如果事先定义好了数据库并且设置了user_id
字段的unique属性,则@JoinColumn(unique = true)
添加与否不会产生任何影响。
1 |
|
双向@OneToOne关联
如果希望能够从User直接引用到BlogMataInfo,则可以给User增加一个类型为BlogMetaInfo的属性,这样两个实体类就是一种双向关联关系了。1
2
3
4
5
6
7
8
9
10
11
public class User {
(strategy=GenerationType.AUTO)
"user") (mappedBy =
private BlogMetaInfo blogMetaInfo;
// ...
}
注意上面的代码中,不仅用@OneToOne标注了blogMetaInfo
,同时还设置mappedBy
属性。
关于mappedBy
属性,需要理解一下两点:
mappedBy = "user"
表示,当前类(User)是通过BlogMetaInfo中user属性与之建立关联的。设置了mappedBy属性的关系标注(各种@XXXToXX),表明当前类是关系的被维护方,而另外一个类则是关系维护方,你可以这样理解:
关系维护方(
BlogMetaInfo
类)对应的是定义外键约束的数据库表关系被维护方(
User
类)对应于外键所在的数据库表
最后需要注意的一个问题是,关系维护方才能够操作两者的关系。反过来说,User
对象即使设置BlogMetaInfo
属性,如果对该对象进行存储,并不会去更新外键关联,因为User是关系被维护方。
双向关联与cascade示例
为了理解以上知识点,这里通过代码示例来进一步。
现在有如下实体类定义:1
2
3
4
5
6
7
8
9
10
11
12
class BlogMetaInfo {
(cascade = CASCADETYPE.ALL)
true) (unique =
private User user;
}
public class User {
"user") (cascade = CASCADETYPE.ALL, mappedBy =
private BlogMetaInfo blogMetaInfo;
}
我们给两个实体的@OneToOne
都设置cascade
属性,表示可以进行级联操作。
1 | BlogMetaInfo blogMetaInfo = new BlogMetaInfo(); |
假设有blogMetaInfoRepository和userRepository两个接口的实例,我们分别进行两种操作:1
2
3// 操作1:保存User
user.setBlogMetaInfo(blogMetaInfo);
userRepository.save(user);
1 | // 操作2:保存BlogMetaInfo |