网站首页 > 精选文章 / 正文
1. 概述
本次教程中,我们会介绍SpringDataJPA中两个实体创建一对一关联映射的三种方法。
关于SpringBoot微服务应用中如何集成SpringDataJPA,请参照:
SpringBoot微服务使用JPA告别繁琐的XML和SQL
2. 前言
本次教程使用这样的场景:在一个用户管理系统中,我们的用户信息比较多,会拆分为用户基本信息表和用户扩展信息表。
基本信息表中我们只保留最常用的几个字段,如:登录名、密码等;扩展信息表中我们会保存用户的一些附加信息,比如性别生日等。
一条用户记录只能有一条附加信息记录,一条附加信息记录也只能对应一条用户记录。
用户类和用户扩展信息类就构成了一对一的关系。
下面我们来看一下如何实现该关系。
3. 使用外键实现
3.1. 使用外键建模的ER图
本例中user的user_extra_id列是user_extra表的外键。
3.2. 在SpringDataJPA中使用外键实现
首先,我们看一下user类,类中不相关的代码我会删除掉:
////// 忽略了包引用
/**
* 用户实体类
*
* @author xtoad
* @date 2020/05/29
*/
@Entity
@Table(appliesTo = "user", comment = "用户表")
@EntityListeners(AuditingEntityListener.class)
public class User extends BaseModel {
private static final long serialVersionUID = -1616905035103332302L;
/**
* 登录名
*/
@Column(nullable = false, unique = true, length = 50, columnDefinition = "VARCHAR(50) COMMENT '登录名'")
private String loginNo;
/**
* 用户扩展信息
*/
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "extra_id", nullable = false, referencedColumnName = "id")
private UserExtra userExtra;
///////// 忽略了其他的属性和方法等
}
我们在userExtra属性上设置了@OneToOne属性。
然后,我们需要使用@JoinColumn注解来配置user表中映射到user_extra表中主键的列的名称。当然你也可以不设置,Hibernate将遵循一些规则来选择默认名称(user_extra_id)。
最后,我们还需要设置一下另外一个实体UserExtra,该实体中不需要使用@JoinColumn注解。因为只需要在外键拥有方设置。直接点就是有外键字段的那一方才需要@JoinColumn。
实体UserExtra代码如下:
///////////////////这里忽略了包引用
/**
* 用户扩展信息实体类
*
* @author xtoad
* @date 2020/05/29
*/
@Entity
@Table(appliesTo = "user_extra", comment = "用户扩展信息表")
@EntityListeners(AuditingEntityListener.class)
public class UserExtra extends BaseModel {
private static final long serialVersionUID = 4833292279765547874L;
/**
* 生日
*/
@Temporal(TemporalType.DATE)
@Column(columnDefinition = "DATE COMMENT '生日'")
private Date birthday;
/**
* 用户信息
*/
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "userExtra")
private User user;
/////////////// 这里忽略了其他属性和方法
}
该类中的user属性,还需要设置@OneToOne注解,因为这是这里建立的是一种双向关系。UserExtra被称为被拥有者。
所有维护操作需要依靠User类来操作,被称为拥有者。
4. 使用共享主键实现
4.1. 使用共享主键建模的ER图
这种实现方式中,我们在user_extra表中建立一个user_id列作为外键,同时也是该表的主键,来映射到user表的id字段上。
这样user_extra 和 user表公用一个主键,可以节省存储空间。
4.2. 在SpringDataJPA中使用共享主键实现
像比较第一种,改动很小,主要是定义外键的一方发生了改变。
User类:
/**
* 用户实体类
*
* @author xtoad
* @date 2020/05/29
*/
@Entity
@Table(appliesTo = "user", comment = "用户表")
@EntityListeners(AuditingEntityListener.class)
public class User extends BaseModel {
private static final long serialVersionUID = -1616905035103332302L;
/**
* 登录名
*/
@Column(nullable = false, unique = true, length = 50, columnDefinition = "VARCHAR(50) COMMENT '登录名'")
private String loginNo;
/**
* 用户扩展信息
*/
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
@PrimaryKeyJoinColumn
private UserExtra userExtra;
UserExtra类:
/**
* 用户扩展信息实体类
*
* @author xtoad
* @date 2020/05/29
*/
@Entity
@Table(appliesTo = "user_extra", comment = "用户扩展信息表")
@EntityListeners(AuditingEntityListener.class)
public class UserExtra extends BaseModel {
private static final long serialVersionUID = 4833292279765547874L;
/**
* 生日
*/
@Temporal(TemporalType.DATE)
@Column(columnDefinition = "DATE COMMENT '生日'")
private Date birthday;
/**
* 用户信息
*/
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@MapsId
@JoinColumn(name = "user_id")
private User user;
}
mappedBy属性放在了User类中,@PrimaryKeyJoinColumn注解指定了使用主键作为关联外键。
同时,在UserExtra类中指定了创建的外键的名称,该字段同时也是会作为UserExtra表的主键。
UserExtra生成的表中不会再出现id列。
然后我们还使用了@MapsId注解,告诉程序自动复制user表的id值。
5. 使用联接表实现
这种方式通常会用在ManyToMany中,事实上OneToOne也是可以采用这种方式的。
OneToOne关系有两种类型:可选 和 强制。
上面两种实现方式都是强制的,就是说两个对象必须是同时存在。
现在我们假设一下上面的例子中:
一个user可以没有扩展信息,扩展信息也可以没有user(当然这例子不是十分恰当,这里只是做个举例)。
我们来一下如何实现。
5.1. 使用联接表建模的ER图
这里我们会创建一个中间表user_and_extra。
表中分别建立两个字段:user_extra_id 作为外键映射到user_extra的主键id,user_id作为外键映射到user的主键id。
其中映射到关系的拥有者user的user_id字段是中间表的主键不能为null。 user_extra_id 是可以为null 的。
这样就能做到user_extra 是可选的。
5.2. 在SpringDataJPA中使用连接表实现
在上面的两种方法中我们使用@JoinColumn注解,这里我们使用@JoinTable注解。
User类:
/**
* 用户实体类
*
* @author xtoad
* @date 2020/05/29
*/
@Entity
@Table(appliesTo = "user", comment = "用户表")
@EntityListeners(AuditingEntityListener.class)
public class User extends BaseModel {
private static final long serialVersionUID = -1616905035103332302L;
/**
* 登录名
*/
@Column(nullable = false, unique = true, length = 50, columnDefinition = "VARCHAR(50) COMMENT '登录名'")
private String loginNo;
/**
* 用户扩展信息
*/
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "user_and_extra",
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "user_extra_id", referencedColumnName = "id")})
private UserExtra userExtra;
}
UserExtra类:
/**
* 用户扩展信息实体类
*
* @author xtoad
* @date 2020/05/29
*/
@Entity
@Table(appliesTo = "user_extra", comment = "用户扩展信息表")
@EntityListeners(AuditingEntityListener.class)
public class UserExtra extends BaseModel {
private static final long serialVersionUID = 4833292279765547874L;
/**
* 生日
*/
@Temporal(TemporalType.DATE)
@Column(columnDefinition = "DATE COMMENT '生日'")
private Date birthday;
/**
* 用户信息
*/
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "userExtra")
private User user;
}
@JoinTable注解告诉Hibernate,在维护关系时使用连接表策略。
我们会在关系的拥有者一方中使用@JoinTable注解。
6. 小结
本篇文章中,我们介绍了在SpringDataJPA中,实现OneToOne关系的三种策略,和使用方法。
大家可以根据自己的实际需求来采取其中的一种。
由于上一篇介绍OneToOne 的文章十分不满意,所以重新捋了一下思路写了这篇。
Tags:manytomany
- 上一篇:many,much,more,most
- 下一篇:一课译词:积少成多
猜你喜欢
- 2024-12-12 much too, too much, too many之间有啥区别?安排
- 2024-12-12 包含介词to的系表介第16组。
- 2024-12-12 爆红歌曲 My Stupid Heart 中英文歌词
- 2024-12-12 英美语言分析研究和解决方案之many和much ((63)
- 2024-12-12 many hands make light work,众擎易举
- 2024-12-12 大牛总结的 Spring Data JPA使用方法,不是一般的香啊
- 2024-12-12 many和much的用法
- 2024-12-12 一课译词:积少成多
- 2024-12-12 many,much,more,most
- 2024-12-12 社交产品中的榜单效应:“1对1” vs “1对多”