一对一关联映射在实际生活中是比较常见的,如人与身份证的关系,通过人这个对象可以找到身份证相关的信
息,也可以通过身份证这个对象找到人相关的信息。
有两种策略可以实现一对一的关联映射:
主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来
维护它们之间的关系,仅通过表的主键来关联;
唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以用来表示一对一关
联关系。
单向一对一主键关联映射
我们先来假设一个场景,从Person中能看到IdCard,也就是把idCard数据表中的主键拿过来当做person表的主
键,单向一对一主键关联映射的实现就是它们的主键相等。一对一关联映射是通过在一方(Person)的对象关系配置
文件加入<one-to-one>元素定义的。这里的方向是由Person--->IdCard。
如下图所示:
下面我们来实现:
Person实体类:
package com.demo.domain;
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;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", idCard=" + idCard + "]";
}
}
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.demo.domain.Person" table="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.demo.domain;
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;
}
@Override
public String toString() {
return "IdCard [id=" + id + ", cardNo=" + cardNo + "]";
}
}
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.demo.domain.IdCard" table="idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>
测试类代码:
package com.demo.test;
import org.hibernate.Session;
import com.demo.domain.HibernateUtils;
import com.demo.domain.IdCard;
import com.demo.domain.Person;
import junit.framework.TestCase;
/**
* 单向一对一关联映射(Person-->IdCard)
* @author Administrator
* @date 2016年12月8日
*/
public class One2OneTest extends TestCase {
public void testSave1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
IdCard idCard = new IdCard();
idCard.setCardNo("1111111");
Person person = new Person();
person.setName("张三");
//建立单向一对一主键映射关联
person.setIdCard(idCard);
//没有抛出TransientObjectException
//是由一对一关联映射的特性决定的,它必须先保存关联对象IdCard
//这样它采用foreign映射策略才能取得关联对象的标识
//也就是它默认了cascade属性
session.save(person);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testSave2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
IdCard idCard = new IdCard();
idCard.setCardNo("2222222");
Person person = new Person();
person.setName("李四");
//建立单向一对一主键映射关联
person.setIdCard(idCard);
//只能将IdCard保存,不能将Person保存
//因为关系的维护端在Person端,IdCard不知道Person的存在
session.save(idCard);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Person person = (Person)session.load(Person.class, 1);
System.out.println("person.name=" + person.getName());
System.out.println("person.cardNo=" + person.getIdCard().getCardNo());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
testSave1()方法:
控制台输出:
数据库显示:
控制台输出:
数据库显示:
控制台输出:
双向一对一主键关联映射
我们先来假设一个场景,双向一对一主键关联映射与单向一对一主键关联映射的区别就是,单向一对一关联映
射,在person端能看到idCard,而idCard不能看到Person端。而双向一对一关联映射就是在idCard端也能看到
person,也就是不但在Person.hbm.xml中加上<one-to-one>标签,同时在IdCard.hbm.xml文件中加上<one-to-one>标
签。
如下图所示:
我们来实现:
Person实体类:同单向一对一主键关联映射的Person实体类一样
Person对象关系映射配置文件:同单向一对一主键关联映射的Person对象关系映射配置文件一样
IdCard实体类:
package com.demo.domain;
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;
}
@Override
public String toString() {
return "IdCard [id=" + id + ", cardNo=" + cardNo + ", person=" + person + "]";
}
}
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.demo.domain.IdCard" table="idCard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
<!-- 一对一关联映射 -->
<one-to-one name="person" />
</class>
</hibernate-mapping>
测试类代码:
package com.demo.test;
import org.hibernate.Session;
import com.demo.domain.HibernateUtils;
import com.demo.domain.IdCard;
import com.demo.domain.Person;
import junit.framework.TestCase;
/**
* 双向一对一关联映射(Person<--->IdCard)
* @author Administrator
* @date 2016年12月8日
*/
public class One2OneTest extends TestCase {
public void testSave1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
IdCard idCard = new IdCard();
idCard.setCardNo("1111111");
Person person = new Person();
person.setName("张三");
//建立一对一关联映射
person.setIdCard(idCard);
//没有抛出TransientObjectException
//是由一对一关联映射的特性决定的,它必须先保存关联对象IdCard
//这样它采用foreign映射策略才能取得关联对象的标识
//也就是它默认了cascade属性
//保存Person对象
session.save(person);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testSave2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Person person = new Person();
person.setName("李四");
IdCard idCard = new IdCard();
idCard.setCardNo("2222222");
//建立一对一关联映射
idCard.setPerson(person);
//保存IdCard对象
session.save(idCard);//人是不会保存进入数据库的,想要保存需要加:person.setIdCard(idCard);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Person person = (Person)session.load(Person.class, 1);
System.out.println("person.name=" + person.getName());
System.out.println("person.cardNo=" + person.getIdCard().getCardNo());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
IdCard idCard = (IdCard)session.load(IdCard.class, 1);
System.out.println("idCard.cardNo=" + idCard.getCardNo());
System.out.println("idCard.person.name=" + idCard.getPerson().getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
testSave1()方法
控制台输出:
数据库显示:
testSave2()方法
控制台输出:
数据库显示:
testLoad1()方法
控制台输出:
testLoad2()方法
控制台输出:
对于双向一对一主键关联映射一直没想明白,在IdCard对象关系映射文件中的<one-to-one>元素中设置了级联属
性为all,保存idCard对象时候,还是保存不了person对象,不知道原因是什么?待解决