学习Hibernate的映射的过程中,觉得最重要的是从过程到对象思维的转换。只是去学那些配置如何来写,没有多大意思。如果真正去面对一个项目,从现实生活中抽象出实体,完成Hibernate的映射应该是一个很好地体验。说归说,那些基础的知识还是要整理总结。这篇博客主要总结一对一映射主键关联的单向和双向。
Hibernate中一对一映射应该说是最简单的,也是最基础的。处理的是现实生活中的一对一这样的实体模型:比如一个人对应一个身份ID。
Hibernate一对一映射实现的策略有两种:一种是主键关联;另一种是唯一外键关联。这两种策略的不同后面会介绍。
Hibernate映射是有方向的,可能是单向也可能是双向,到底是哪种,需要由具体业务来确定。怎么来理解单向和双向呢?先看一张UML图:
在这张图Person和IdCard的关系是单向的,Person可以看到IdCard,也就是查到了Person也就知道了IdCard了。反过来是不能的。
主键关联:原理是让两个实体的主键一致,这样不需要加入多余的字段。
单向:以上图为例
Person端:
package com.bjpowernode.hibernate;
public class Person {
private int id;
private String name;
private IdCard idCard;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.Person" table="t_person">
<id name="id">
<!-- 采用foreign生成策略,forgeign会取得关联对象的标识 -->
<generator class="foreign">
<!-- property只关联对象 -->
<param name="property">idCard</param>
</generator>
</id>
<property name="name"/>
<!--
one-to-one指示hibernate如何加载其关联对象,默认根据主键加载
也就是拿到关系字段值,根据对端的主键来加载关联对象
constrained="true表示,当前主键(person的主键)还是一个外键
参照了对端的主键(IdCard的主键),也就是会生成外键约束语句
-->
<one-to-one name="idCard" constrained="true"/>
</class>
</hibernate-mapping>
IdCard端:
package com.bjpowernode.hibernate;
public class IdCard {
private int id;
private String cardNo;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.IdCard" table="t_idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>
映射出的表结构如下:
双向:UML图:
Person端没有变化:
IdCard端:
package com.bjpowernode.hibernate;
public class IdCard {
private int id;
private String cardNo;
private Person person;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCardNo() {
return cardNo;
}
public void setCardNo(String cardNo) {
this.cardNo = cardNo;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.bjpowernode.hibernate.IdCard" table="t_idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
<one-to-one name="person"/>
</class>
</hibernate-mapping>
注意:文章中的例子都是Person端在维护一对一的关系,而且是通过Foreign主键生成策略得关联对象的标识(也可以理解为默认了casade属性),所以在save操作时,如果保存Person,它一定会先将IdCard也保存了,不会没有抛出TransientObjectException。如果保存的是IdCard,则不会保存Person,因为IdCard并不维护关系。
总结:一对一主键关联:通过foreign策略让两个实体的主键一致,无需加入多余字段来维护。