典型的实例:一个人有一个身份证,而一个身份证只属于某一个人。以此为模型实现一对一关系的映射。下面的实例先以主键进行关联:
主键关联 :从表的主键同时又作为外键参考主表的主键。比如在下面的实例中,人作为主表,身份证作为从表。
>> 步骤一,建立Person 类及相关的实体配置文件。
package com.asm.hibernate.domain;
public class Person {
private int id ;
private String name ;
private IdCard idCard ;
} ... 省略相应的get/set 方法。
配置文件:person.hbm.xml 和前面的配置一样,只需要留意下 </ one-to-one > 元素,内容如下:
< hibernate-mapping package = "com.asm.hibernate.domain" >
< class name = "Person" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "name" ></ property >
< one-to-one name = "idCard" ></ one-to-one >
</ class >
</ hibernate-mapping >
>> 步骤二,建立IdCard 类及相关的实体配置文件。
package com.asm.hibernate.domain;
import java.util.Date;
public class IdCard {
private int id ;
private Date validity ;
private Person person ;
} ... 省略相应的get/set 方法。
从表配置文件:IdCard.hbm.xml ,内容如下:
< hibernate-mapping package = "com.asm.hibernate.domain" >
< class name = "IdCard" >
< id name = "id" >
< generator class = "foreign" >
< param name = "property" > person </ param >
</ generator >
</ id >
< property name = "validity" ></ property >
< one-to-one name = "person" constrained = "true" ></ one-to-one >
</ class >
</ hibernate-mapping >
配置文件说明:由于上面提到的是采取主键关联,即是说这里的id 即是主键,同时也是(关联相关表的)外键,因此,以下对id 的生成采取了”foreign” 方式,其实这种方式也就指明了主键同时为外键。下面的<param> 指定了外键的参考信息,此元素中的内容指明了它参考<one-to-one> 的person 。 注意在< one-to-one name = "person" constrained = "true" > 中设定了constrained 属性,其作用是说明该配置文件所映射表的主键同时作为外键,参照关联类对应表的主键。设定了此属性可用“show create table idcard ”查看表idcard 的定义发生了变化。
>> 步骤三,修改主配置文件,关联上面的实体配置文件。
>> 步骤四,编写测试类, OneToOneTest.java 内容如下:省略导入的包。
package com.asm.hibernate.test;
public class OneToOneTest {
public static void main(String[] args) {
add ();
}
static void add() {
Session s = null ;
Transaction tr = null ;
try {
s = HibernateUtil.getSession ();
tr = s.beginTransaction();
Person person = new Person();
person .setName( "pName" );
IdCard idCard = new IdCard();
idCard.setValidity( new Date());
// 分别注释掉以下两句,看程序执行情况
person .setIdCard(idCard);
idCard.setPerson(person );
s.save(person );
s.save(idCard);
tr.commit();
} finally {
if (s != null )
s.close();
}
}
}
说明: 留意上面的注释,如果注释掉第一句,发现一切正常,因为主对象是可以没有此属性,它的实体配置文件也基本与前面一样。而如果注释掉下面一句,将会报错,原因是“ attempted to assign id from null one-to-one property: person ”,IdCard 的实体配置文件关联了一个表,而它采取主键关联,而主键关联要依赖于person 属性的id ,如果这里注释掉,即没有了此属性,它也关联不了相应的id 。简单的说,IdCard 来要关联Person ,我们称它为从对象,而person 并不关联谁,我们称为主对象。现在只要记住,从对象关联了表(关联了主对象),必须设定它所关联的主对象属性
>> 步骤五,编写两个查询方法,一个查询主对象,主要代码:
static Person query( int id) {
Session s = null ;
Transaction tr = null ;
try {
s = HibernateUtil.getSession ();
tr = s.beginTransaction();
Person p = (Person) s.get(Person. class , id);
System. out .println( " 身份证有效期:" + p.getIdCard().getValidity());
tr.commit();
return p;
} finally {
if (s != null )
s.close();
}
}
然后再在main 方法中调用此方法,并开启控制台数据库库语言显示后,可以从控制台看出查询主对象只select 一次;再增加一个查询从对象的方法,主要代码:
static IdCard query2( int id) {
Session s = null ;
Transaction tr = null ;
try {
s = HibernateUtil.getSession ();
tr = s.beginTransaction();
IdCard idCard = (IdCard) s.get(IdCard. class , id);
// System. out .println( " 人的名字:" + idCard.getPerson().getName());
// 去掉上一句注释后,发现会查询两次。
tr.commit();
return idCard;
} finally {
if (s != null )
s.close();
}
}
同样在main 方法中调用此方法, 并开启控制台数据库库语言显示后。 从控制台看出也只会查询一次,但是如果去掉注释后发现会查询两次。 接着,在此例的基础上修改成外键关联。
外键关联 :从表的主键并不作为外键参考主表的主键,而是将其它字段作为外键参的主键。
其实在上例的基础上,我们只需要修改IdCard.hbm.xml 配置文件即可,修改后的内容如下:
< hibernate-mapping package = "com.asm.hibernate.domain" >
< class name = "IdCard" >
< id name = "id" >
< generator class = "native" />
</ id >
< property name = "validity" ></ property >
< many-to-one name = "person" column = "person_id" unique = "true" />
</ class >
</ hibernate-mapping >
说明:由于采取了外键关联,所以 这里的从表的主键将不再作为外键参考主表的主键,所以它会采取一般的方式生成主键,即<id> 生成和以前的那此相同采取“native ”方式。 另注意到< many-to-one >, 发现增加了unique 有属性,这样尽管是多对一,但能有效保证实质是一对一。 这时运行原OneToOneTest ,发现仍是和以前一样。 如果我们再修改Person 的实体配置文件<one-to-one> 如下:
< one-to-one name = "idCard" property-ref = "person" /> 特别要注意到 property-ref 属性。可以结合执行后表的结构来看。其实如果注释掉此句,其结果就是通过身份证可以查看到Person 的相关信息,但是通过Person 却不能找到身份证信息,因为Hibernate 完全依赖实体配置文件(映射文件)。注释掉当然就不能找到。 而事实上这时在OneToOne 中调用query 方法,会发现出现空指针异常。 其实在前面的关联关系中,最终都是实现了双向关联,而这里如果注释掉此句,正好成了单向关联的一个例证。