基于外键的 1-1 关联关系:在双向的一对一关联中,需要在关系被维护端(inverse side)中的 @OneToOne 注解中指定 mappedBy,以指定是这一关联中的被维护端。同时需要在关系维护端(owner side)建立外键列指向关系被维护端的主键列。
【1】Manager与Department
一个部门与一个经理唯一对应,由部门维持关联关系,即以Department为主,Manager为从。
Manager实体类如下:
@Table(name="JPA_MANAGERS") @Entity public class Manager { private Integer id; private String mgrName; private Department dept; @GeneratedValue @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="MGR_NAME") public String getMgrName() { return mgrName; } public void setMgrName(String mgrName) { this.mgrName = mgrName; } //对于不维护关联关系, 没有外键的一方, 使用 @OneToOne 来进行映射, //建议设置 mappedBy=true @OneToOne(mappedBy="mgr") public Department getDept() { return dept; } public void setDept(Department dept) { this.dept = dept; } }
Department实体类如下:
@Table(name="JPA_DEPARTMENTS") @Entity public class Department { private Integer id; private String deptName; private Manager mgr; @GeneratedValue @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="DEPT_NAME") public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } //使用 @OneToOne 来映射 1-1 关联关系。 //若需要在当前数据表中添加外键则需要使用 @JoinColumn 来进行映射. //注意, 1-1 关联关系, 所以需要添加 unique=true @JoinColumn(name="MGR_ID", unique=true) @OneToOne(fetch=FetchType.LAZY) public Manager getMgr() { return mgr; } public void setMgr(Manager mgr) { this.mgr = mgr; } }
【2】双向1对1持久化操作
代码示例如下:
@Test public void testOneToOnePersistence(){ Manager mgr = new Manager(); mgr.setMgrName("M-BB"); Department dept = new Department(); dept.setDeptName("D-BB"); //设置关联关系 mgr.setDept(dept); dept.setMgr(mgr); //执行保存操作 entityManager.persist(mgr); entityManager.persist(dept); }
如上所示,先保存不维持关联关系的一端,控制台输出如下:
如果先保存dept,即维持关联关系的一端:
//执行保存操作 entityManager.persist(dept); entityManager.persist(mgr);
综上可知,双向 1-1 的关联关系, 建议先保存不维护关联关系的一方, 即没有外键(@JoinColumn)的一方, 这样不会多出 UPDATE 语句。
【3】双向一对一获取操作
① 先获取维持关联关系的一端(Department),代码示例如下:
@Test public void testOneToOneFind(){ Department dept = entityManager.find(Department.class, 1); System.out.println(dept.getDeptName()); System.out.println(dept.getMgr().getClass().getName()); }
- 如果加载策略为EAGER,控制台输出如下:
默认情况下, 若获取维护关联关系的一方, 则会通过左外连接获取其关联的对象。 但可以通过 @OntToOne 的 fetch 属性来修改加载策略。
- 如果加载策略为懒加载,控制台输出如下:
另外对比两次获取的manager对象可知,懒加载是获取的为代理对象,EAGER时获取的为实际对象。
② 如果先获取不维持关联关系的一方。
@Test public void testOneToOneFind2(){ Manager mgr = entityManager.find(Manager.class, 1); System.out.println(mgr.getMgrName()); System.out.println(mgr.getDept().getClass().getName()); }
- EAGER策略时,控制台输出如下:
@OneToOne(mappedBy="mgr",fetch=FetchType.EAGER) public Department getDept() { return dept; }
默认情况下, 若获取不维护关联关系的一方, 则也会通过左外连接获取其关联的对象。
可以通过 @OneToOne 的 fetch 属性来修改加载策略. 但依然会再发送 SQL 语句来初始化其关联的对象。
这说明在不维护关联关系的一方, 不建议修改 fetch 属性。