一对一关系映射即为关系双方都含有对方一个引用,其实在生活中一对一关系也很常见,比如人和身份证,学生和学号等,都是一对一的关系映射, 一对一映射分为单向的和双向的,没种关系映射又可以分为主键关联映射 , 唯一外键关联映射。

一:主键关联映射

一般一对一主键关联映射通过foreign主键生成器使用另外一个相关联的对象的标识符。通常和<one-to-one>联合起来使用。一对一主键关联映射原理:让两个实体的主键一样,这样就不需要加入多余的字段。此种关联映射有一定的缺点:单向一对一主键关联实际上限制很多,因为你只有IdCard插入了那才能有这个Person.我们看一下具体示例:

                        

Java程序员从笨鸟到菜鸟之(五十八)细谈Hibernate(九)hibernate一对一关系映射_hibernate

 

根据上面的关系类图,我们再来看一下实体类的定义

 

实体IdCard.java


[java]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


public class IdCard

{

private String id;

private int number;

private Person person;

}



 

实体:Person.java


[java]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


public class Person

{

private String id;

private String name;

private IdCard idCard;

}


IdCard的配置文件:


[html]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​





<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<classname="com.jzq.hibernate2.IdCard" table="t_IdCard">

<!--这里我们应该注意的是此处的主键生成策略,这里的主键是利用的外键生成策略生成的,这里的主键关联着idcard表中的主键,也就是说,保证Person和idcard的主键相同。

-->
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>



person的配置文件:



[html]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<classname="com.jzq.hibernate2.IdCard" table="t_Person">
<!--id引用外部主键 -->
<id name="id">
<generatorclass="foreign">
<paramname="property">idCard</param>
</generator>
</id>
<property name="name"/>
<one-to-one name="idCard"constrained="true"/>
</class>
</hibernate-mapping>

注:one-to-one标签告诉hibernate根据主键加载引用对象 , 把person中的主键拿到idCard表中进行查找,然后把查到的信息加载到引用对象中采用一对一主键约束,那么必须设置constrained属性,表示当前主键作为外键参照了该属性在一对一主键关联映射中默认问级联属性


配置完了以后我们来看一下具体的增删改查操作:


      


[java]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


Person person = newPerson();

person.setName("zhangsan");

IdCard idCard = new IdCard();

idCard.setNumber(987654);

person.setIdCard(idCard);

idCard.setPerson(person);

Session session = sessionFactory.openSession();

Transaction tx = null;



try

{
tx =session.beginTransaction();
session.save(person);
tx.commit();
}
catch(Exception ex)
{
ex.printStackTrace();
if(null != tx)
{
tx.rollback();

}
}
finally
{
session.close();
}

//---------------------------------------------------

// Session session = sessionFactory.openSession();
// Transaction tx = null;
// Person person = null;
// try
// {
// tx =session.beginTransaction();
// person =(Person)session.get(Person.class,"402881ec2ebd7e77012ebd7e79e40001");
// tx.commit();
// }
// catch(Exception ex)
// {
// if(null != tx)
// {
// tx.rollback();
// }
// }
// finally
// {
// session.close();
// }
// System.out.println(person.getName());
// System.out.println(person.getIdCard().getNumber());

//-----------------------------------------

// Session session = sessionFactory.openSession();
// Transaction tx = null;
//
// Person person = null;
// try
// {
// tx =session.beginTransaction();
//
// person =(Person)session.get(Person.class,"402881ec2ebd7e77012ebd7e79e40001");
//
// person.setName("lisi");
// tx.commit();
// }
// catch(Exception ex)
// {
// if(null != tx)
// {
// tx.rollback();
// }
// }
// finally
// {
// session.close();
// }
// System.out.println(person.getName());
//-----------------------------------------

// Session session = sessionFactory.openSession();
// Transaction tx = null;
// Person person = null;
// try
// {
// tx =session.beginTransaction();
// person =(Person)session.get(Person.class,"402881ec2ebd7e77012ebd7e79e40001");
// session.delete(person);
// tx.commit();
// }
// catch(Exception ex)
// {
// if(null != tx)
// {
// tx.rollback();
// }
// }
// finally
// {
// session.close();
// }



         通过执行查询,我们可以发现,hibernate的一对一默认执行的检索方式是外连接检索方式,如果我们不想用外连接检索方式,我们可以设置一下one-to-one的fetch属性,他有两个值,一个是select,一个是jion。我想大家通过字面也能猜出他们所对应的检索方式。

        Hibernate一对一中也可以设置延迟加载,一对一默认使用的是立即加载,如果需要使用延迟加载,那么需要在one-to-one元素中将constrained属性设为true,并且将待加载的一方的class元素中的lazy属性设为true(或者不去设置,因为该属性默认值就是true)。一对一加载时默认使用左外连接,可以通过修改fetch属性为select修改成每次发送一条select语句的形式。

 

唯一外键关联映射:其实它是一对多的特殊情况,它基本和一对多是完全相同的,只不过需要配置一个属性而已。其本质上是一对多的蜕化形式。在many-to-one元素中增加unique=”true”属性就变成了一对一。

 

二、一对唯一外键关联映射——单向

1.一对唯一外键关联映射是多对一关联映射的特例,可以采用<many-to-one>标签,指定多的一端的unique=true,这样就限 制了多的一端的多重性为一,通过这种手段映射一对一唯一外键关联

2.领域模型图:

                        

Java程序员从笨鸟到菜鸟之(五十八)细谈Hibernate(九)hibernate一对一关系映射_一对一_02

3.配置

Person.hbm.xml:


[html]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


<class name="com.bjsxt.hibernate.Person"table="t_person">

<id name="id">

<generator class="native"/>

</id>

<property name="name"/>

<many-to-one name="idCard" unique="true"/>

</class>

 

IDCard.hbm.xml:


[html]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


<class name="com.bjsxt.hibernate.IdCard"table="t_idcard">

<id name="id">

<generator class="native"/>

</id>

<property name="cardNo"/>

</class>

 

三、 一对唯一外键关联映射——双向

1.一对一唯一外键关联双向,需要在另一端(idcard),添加<one-to-one>标签,指示hibernate如何加载其关联对象,默认根据主键加载person,外键关联映射中,因为两个实体采用的是person的外键维护的关系,所以不能指定主键加载person,而要根据person的外键加载,所以采用如下映射方式:

<one-to-one name="person"property-ref="idCard"/>

2.领域模型图:

                        

Java程序员从笨鸟到菜鸟之(五十八)细谈Hibernate(九)hibernate一对一关系映射_主键_03

3.具体配置:

 

Person.hbm.xml:


[html]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


<class name="com.bjsxt.hibernate.Person"table="t_person">

<id name="id">

<generator class="native"/>

</id>

<property name="name"/>

<many-to-one name="idCard" unique="true"/>

</class>

 

IDCard.hbm.xml


[html]​ view plain​​​ ​​​copy​​​ ​​​print​​​ ​​​?​


<class name="com.bjsxt.hibernate.IdCard"table="t_idcard">

<id name="id">

<generator class="native"/>

</id>

<property name="cardNo"/>

<one-to-one name="person"property-ref="idCard"/>

</class>