JPA实体映射——一对一关系映射
前几节我们介绍了一对多的关系,今天我们学习一对一关系以及这种映射方式的最佳实践,先上业务实例图。
从图中可以看出,研究所和社交账号有一对一的关系,部门和社交账号也有一对一的关系,我们选用研究所和社交账号的关系来说明问题。
###Bidirectional @OneToOne ###研究所实体 mport javax.persistence.*; import java.io.Serializable; import java.util.HashSet; import java.util.Set; @Entity(name = "Institute") @Table(name = "institutes") public class Institute implements Serializable { @Id @GeneratedValue private Long id; @OneToMany( mappedBy = "institute", cascade = CascadeType.ALL, orphanRemoval = true ) private Set这里增加了一个社交账号的属性,采用延迟加载策略。自定义了一个setSocialProfile()的方法。
###社交账号实体 import javax.persistence.*; import java.io.Serializable; @Entity( name = "SocialProfile") @Table(name = "socialprofiles") public class SocialProfile implements Serializable { @Id @GeneratedValue private long id; private String shortName; @OneToOne(fetch = FetchType.LAZY) @JoinColumn() private Institute institute; public SocialProfile(){} public SocialProfile(String shortName) { this.shortName = shortName; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getShortName() { return shortName; } public void setShortName(String shortName) { this.shortName = shortName; } public Institute getInstitute() { return institute; } public void setInstitute(Institute institute) { this.institute = institute; } }测试代码:
@Test public void testOneToOneSave() { Institute institute = new Institute("深圳研究所"); institute.setSocialProfile(new SocialProfile("深圳研究所-社交账号")); InstituteDAO dao = new InstituteDAO(); dao.save(institute); }日志信息:
Hibernate: create table institutes ( id bigint not null, name varchar(255), primary key (id) ) Hibernate: create table socialprofiles ( id bigint not null, shortName varchar(255), institute_id bigint, primary key (id) ) Hibernate: create sequence hibernate_sequence start with 1 increment by 1 Hibernate: alter table socialprofiles add constraint FK8o0yil50tmsnyfbqgrlj7b5v0 foreign key (institute_id) references institutes Hibernate: call next value for hibernate_sequence Hibernate: call next value for hibernate_sequence Hibernate: insert into institutes (name, id) values (?, ?) Hibernate: insert into socialprofiles (institute_id, shortName, id) values (?, ?, ?)可以看出在一对一关系时候,在socialprofiles上添加了一个外键字段institute_id,保存的时候也是将双方关系实体保存。
我们在看看查询
@Test public void testOneToOneQuery() { Institute institute = new Institute("深圳研究所"); institute.setSocialProfile(new SocialProfile("深圳研究所-社交账号")); InstituteDAO dao = new InstituteDAO(); dao.save(institute); long id = institute.getId(); dao.queryById(id); }日志信息:
Hibernate: select institute0_.id as id1_1_0_, institute0_.name as name2_1_0_ from institutes institute0_ where institute0_.id=? Hibernate: select socialprof0_.id as id1_2_0_, socialprof0_.institute_id as institut3_2_0_, socialprof0_.shortName as shortNam2_2_0_ from socialprofiles socialprof0_ where socialprof0_.institute_id=?可以看出这里我只是查询研究所信息,但是实际上也将社交账号的信息一并查出,这种方式在一些场景中是合适的,但是有的时候我也许需要延迟加载。因此,我总结如下:
1、这种实现方式在某些场景下是合适的,但是存在两个问题。
2、多了一个外键字段,实际上在一对一关系的时候,外键字段是可以共享的。
3、单向关联的时候延迟加载可行,双向关联的时候延迟加载不可用。
###双向一对一关联最佳实践
最佳实践的使用方法如下,只需要修改SocialProfile实体就可以啦,代码如下:
import javax.persistence.*; import java.io.Serializable; @Entity( name = "SocialProfile") @Table(name = "socialprofiles") public class SocialProfile implements Serializable { @Id @GeneratedValue private long id; private String shortName; @OneToOne(fetch = FetchType.LAZY) @MapsId @JoinColumn(name = "id") private Institute institute; public SocialProfile(){} public SocialProfile(String shortName) { this.shortName = shortName; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getShortName() { return shortName; } public void setShortName(String shortName) { this.shortName = shortName; } public Institute getInstitute() { return institute; } public void setInstitute(Institute institute) { this.institute = institute; } }增加使用了一个注解@MapsId,同时将外键映射到id,实际上就是将外键与主键公用。这种方法称为共享主键,也就是一对一的双发实体共享一个主键,在这种情况下,甚至都可以不需要双向关联,有兴趣的可以试试这种情况。