hibernate 一对一(One-to-One)

一对一(one-to-one)实例(Person-IdCard)

一对一的关系在数据库中表示为主外关系.例如.人和身份证的关系.每个人都对应一个身份证号.我们应该两个表.一个是关于人信息的表(Person).别外一个是身份证相关信息的表(id_card).id_card表的主键对应该Person表的主键id,也是Person表的外键.有人才能有身份证.所以此例中Person是主表,id_card表为从表。

hibernate的一对一关系有两种形式,一种是共享主键方式,另一种是唯一外键方式.

一、共享主键方式实现一对一

1.实体类设计如下:

Person类:

Java代码

1.packagecom.reiyen.hibernate.domain;

2.

3.publicclassPerson{

4.

5.privateintid;

6.privateStringname;

7.privateIdCardidCard;

8.

9.//setter和getter方法

10.}

packagecom.reiyen.hibernate.domain;

publicclassPerson{

privateintid;

privateStringname;

privateIdCardidCard;

//setter和getter方法

}IdCard类:

Java代码

1.packagecom.reiyen.hibernate.domain;

2.

3.publicclassIdCard{

4.

5.privateintid;

6.privateDateauthorizeDate;

7.privatePersonperson;

8.

9.//setter和getter方法

10.}

packagecom.reiyen.hibernate.domain;

publicclassIdCard{

privateintid;

privateDateauthorizeDate;

privatePersonperson;

//setter和getter方法

}2.映射文件:

Person.hbm.xml文件如下:

Xml代码

1.<?xmlversion="1.0"?>

2.<!DOCTYPEhibernate-mappingPUBLIC

3."-//Hibernate/HibernateMappingDTD3.0//EN"

4."http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

5.<hibernate-mappingpackage="com.reiyen.hibernate.domain">

6.<classname="Person">

7.<idname="id">

8.<generatorclass="native"/>

9.</id>

10.<propertyname="name"/>

11.<one-to-onename="idCard"/>

12.</class>

13.</hibernate-mapping>

<?xmlversion="1.0"?>

<!DOCTYPEhibernate-mappingPUBLIC

"-//Hibernate/HibernateMappingDTD3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mappingpackage="com.reiyen.hibernate.domain">

<classname="Person">

<idname="id">

<generatorclass="native"/>

</id>

<propertyname="name"/>

<one-to-onename="idCard"/>

</class>

</hibernate-mapping>IdCard.hbm.xml文件如下:

Xml代码

1.<?xmlversion="1.0"?>

2.<!DOCTYPEhibernate-mappingPUBLIC

3."-//Hibernate/HibernateMappingDTD3.0//EN"

4."http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

5.<hibernate-mappingpackage="com.reiyen.hibernate.domain">

6.<classname="IdCard"table="id_card">

7.<idname="id">

8.<!--id_card的主键来源person,也就是共享idCard的主键-->

9.<generatorclass="foreign">

10.<paramname="property">person</param>

11.</generator>

12.</id>

13.<propertyname="authorizeDate"column="authorize_date"/>

14.<!--one-to-one标签的含义,指示hibernate怎么加载它的关联对象,默认根据主键加载,

15.constrained="true",表明当前主键上存在一个约束,id_card的主键作为外键参照了person-->

16.<one-to-onename="person"constrained="true"></one-to-one>

17.</class>

18.</hibernate-mapping>

<?xmlversion="1.0"?>

<!DOCTYPEhibernate-mappingPUBLIC

"-//Hibernate/HibernateMappingDTD3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mappingpackage="com.reiyen.hibernate.domain">

<classname="IdCard"table="id_card">

<idname="id">

<!--id_card的主键来源person,也就是共享idCard的主键-->

<generatorclass="foreign">

<paramname="property">person</param>

</generator>

</id>

<propertyname="authorizeDate"column="authorize_date"/>

<!--one-to-one标签的含义,指示hibernate怎么加载它的关联对象,默认根据主键加载,

constrained="true",表明当前主键上存在一个约束,id_card的主键作为外键参照了person-->

<one-to-onename="person"constrained="true"></one-to-one>

</class>

</hibernate-mapping>3.在hibernate.cfg.xml文件中注册映射文件:

Xml代码

1.<mappingresource="com/reiyen/hibernate/domain/Person.hbm.xml"/>

2.<mappingresource="com/reiyen/hibernate/domain/IdCard.hbm.xml"/>

<mappingresource="com/reiyen/hibernate/domain/Person.hbm.xml"/>

<mappingresource="com/reiyen/hibernate/domain/IdCard.hbm.xml"/>4.测试类如下:

Java代码

1.publicclassOne2One{

2.publicstaticvoidmain(String[]args){

3.Personperson=add();

4.System.out.println("peronname:"+person.getName());

5.}

6.

7.staticPersonadd(){

8.Sessionsession=null;

9.Transactiontran=null;

10.try{

11.session=HibernateUtil.getSession();

12.IdCardidCard=newIdCard();

13.idCard.setAuthorizeDate(newDate());

14.

15.Personp=newPerson();

16.p.setName("person1");

17.p.setIdCard(idCard);//1

18.idCard.setPerson(p);//2

19.tran=session.beginTransaction();

20.session.save(p);

21.session.save(idCard);

22.tran.commit();

23.returnp;

24.}finally{

25.if(session!=null)

26.session.close();

27.}

publicclassOne2One{

publicstaticvoidmain(String[]args){

Personperson=add();

System.out.println("peronname:"+person.getName());

}

staticPersonadd(){

Sessionsession=null;

Transactiontran=null;

try{

session=HibernateUtil.getSession();

IdCardidCard=newIdCard();

idCard.setAuthorizeDate(newDate());

Personp=newPerson();

p.setName("person1");

p.setIdCard(idCard);//1

idCard.setPerson(p);//2

tran=session.beginTransaction();

session.save(p);

session.save(idCard);

tran.commit();

returnp;

}finally{

if(session!=null)

session.close();

}控制台打印信息如下所示:

Hibernate:insertintoPerson(name)values(?)

Hibernate:insertintoid_card(authorize_date,id)values(?,?)

personname:person1

数据库表id_card的创建语句如下所示:(重点注意红色字体部分)

DROPTABLEIFEXISTS`test`.`id_card`;

CREATETABLE`test`.`id_card`(

`id`int(11)NOTNULL,

`authorize_date`datetimeDEFAULTNULL,

PRIMARYKEY(`id`),

KEY`FK627C1FB4284AAF67`(`id`),

CONSTRAINT`FK627C1FB4284AAF67`FOREIGNKEY(`id`)REFERENCES`person`(`id`)

)ENGINE=InnoDBDEFAULTCHARSET=utf8;

数据库中记录如下所示:

mysql>select*fromperson;

+----+---------+

|id|name|

+----+---------+

|1|person1|

+----+---------+

1rowinset(0.00sec)

mysql>select*fromid_card;

+----+---------------------+

|id|authorize_date|

+----+---------------------+

|1|2010-03-2301:07:25|

+----+---------------------+

1rowinset(0.00sec)

在测试时一定要注意写上这句代码:

Java代码

1.idCard.setPerson(p);

idCard.setPerson(p);这是让从对象关联上它所从属的主对象。如果没有这句话,则会抛出如下异常:

org.hibernate.id.IdentifierGenerationException:attemptedtoassignidfromnullone-to-oneproperty:person

5.查询测试,测试类如下:

Java代码

1.publicclassOne2One{

2.publicstaticvoidmain(String[]args){

3.query(1);

4.}

5.

6.staticvoidquery(Integerid){

7.Sessionsession=null;

8.try{

9.session=HibernateUtil.getSession();

10.Personperson=(Person)session.get(Person.class,id);//4

11.System.out.println(person.getIdCard().getAuthorizeDate());//3

12.//IdCardidCard=(IdCard)session.get(IdCard.class,id);//1

13.//System.out.println(idCard.getPerson().getName());//2

14.}finally{

15.if(session!=null)

16.session.close();

17.}

18.}

19.}

publicclassOne2One{

publicstaticvoidmain(String[]args){

query(1);

}

staticvoidquery(Integerid){

Sessionsession=null;

try{

session=HibernateUtil.getSession();

Personperson=(Person)session.get(Person.class,id);//4

System.out.println(person.getIdCard().getAuthorizeDate());//3

//IdCardidCard=(IdCard)session.get(IdCard.class,id);//1

//System.out.println(idCard.getPerson().getName());//2

}finally{

if(session!=null)

session.close();

}

}

}执行此测试类时,控制台打印信息如下所示:

Hibernate:selectperson0_.idasid3_1_,person0_.nameasname3_1_,idcard1_.idasid4_0_,idcard1_.authorize_dateasauthorize2_4_0_fromPersonperson0_leftouterjoinid_cardidcard1_onperson0_.id=idcard1_.idwhereperson0_.id=?

2010-03-2321:46:07.0

从打印的SQL语句可以看出,一对一关系查询主对象时,然后得到主对象中的从属对象,通过leftjoin一次就把查询结果查询出来了,因为从对象从属于主对象。而一对多,多对一关系时,从打印的SQL语句可知,它经过了两次查询才将查询结果查询出来。这是它们的区别。如果一对一关系中先查询从属对象,然后得到从属中的主对象时(即把上面测试类中的注释1,2都去掉再运行),控制台打印信息如下:

Hibernate:selectidcard0_.idasid4_0_,idcard0_.authorize_dateasauthorize2_4_0_fromid_cardidcard0_whereidcard0_.id=?

Hibernate:selectperson0_.idasid3_1_,person0_.nameasname3_1_,idcard1_.idasid4_0_,idcard1_.authorize_dateasauthorize2_4_0_fromPersonperson0_leftouterjoinid_cardidcard1_onperson0_.id=idcard1_.idwhereperson0_.id=?

person1

从打印的SQL可以看出,这种先查从对象,然后得到从属对象中的主对象,要经过两次查询才能得到查询结果。

如果只把测试程序中注释1去掉运行,则只会执行上面两次查询中的第一次查询。

6.one-to-one(元素)懒加载分析:

必须同时满足下面的三个条件时才能实现懒散加载:1).lazy!=false(lazy缺省方式就!=false)2).constrained=true3).fetch=select(fetch缺省方式即为select)

(因为主表不能有constrained=true,所以主表没有懒加载功能)。能够懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理对象)的属性(getId和getClass除外)时,hibernate会初始化这些代理,或用Hibernate.initialize(proxy)来初始化代理对象;当相关联的session关闭后,再访问懒加载的对象将会出现异常。

测试:(1).注释掉查询测试程序中标记为3的语句,运行程序,控制台打印信息如下:

Hibernate:selectperson0_.idasid4_1_,person0_.nameasname4_1_,idcard1_.idasid5_0_,idcard1_.authorize_dateasauthorize2_5_0_fromPersonperson0_leftouterjoinid_cardidcard1_onperson0_.id=idcard1_.idwhereperson0_.id=?

说明查询主对象Person时没有懒加载特性,因此它通过leftouterjoinid_card表,同时把它的从对象IdCard也查询出来了。那为什么一对一中查询主对象时,不能实现懒加载呢??大家可以看看person表的结构,你从表结构中根本不能决断出Person对象有没有相应的IdCard对象,所以它无法给setIdCard赋值(hibernate不能想当然的认为你有值,给你new一个代理对象给它),所以它一定要去查询相关联的对象表,看是否有与此Person对应的IdCard记录。而如果查询的是从对象IdCard时,因为idcard中的id是一个person表的一个外键,所以它必定有一个相对应的Person对象(因为有constrained=true),所以它可以先返回给你一个代理对象,当你真正需要Person对象的数据时,它再去查询数据库。

(2).注释掉查询测试程序中标记为3,4的语句,同进将标记为1的语句前的注释去掉再运行程序,控制台打印信息如下:

Hibernate:selectidcard0_.idasid5_0_,idcard0_.authorize_dateasauthorize2_5_0_fromid_cardidcard0_whereidcard0_.id=?

从打印信息可以看出,查询从对象IdCard时实现了懒加载功能,因为它只查询了IdCard对象,而关联的Person对象它没有进行查询。

(3).如果在(2)基础上将标记为2的语句前的注释也去掉再运行程序,控制台打印信息如下:

Hibernate:selectidcard0_.idasid5_0_,idcard0_.authorize_dateasauthorize2_5_0_fromid_cardidcard0_whereidcard0_.id=?

Hibernate:selectperson0_.idasid4_1_,person0_.nameasname4_1_,idcard1_.idasid5_0_,idcard1_.authorize_dateasauthorize2_5_0_fromPersonperson0_leftouterjoinid_cardidcard1_onperson0_.id=idcard1_.idwhereperson0_.id=?

person1

它就进行了两次查询,将IdCard关联的Person对象也进行了查询。因为访问这些懒加载对象(代理对象)的属性(getId和getClass除外)时,hibernate会初始化这些代理.

(4).如果修改IdCard.hbm.xml映射文件,增加fetch="join",如下所示:

Xml代码

1.<one-to-onename="person"constrained="true"fetch="join"/>

<one-to-onename="person"constrained="true"fetch="join"/>

再按(2)进行测试,此时控制台打印信息如下:

Hibernate:selectidcard0_.idasid5_1_,idcard0_.authorize_dateasauthorize2_5_1_,person1_.idasid4_0_,person1_.nameasname4_0_fromid_cardidcard0_innerjoinPersonperson1_onidcard0_.id=person1_.idwhereidcard0_.id=?

此时查询从对象IdCard时也不再懒加载了,通过innerjoin一次性将主从对象都查询出来。

(5).如果修改IdCard.hbm.xml映射文件,增加lazy="false",如下所示:

Xml代码

1.<one-to-onename="person"constrained="true"lazy="false"/>

<one-to-onename="person"constrained="true"lazy="false"/>再按(2)进行测试,此时控制台打印信息如下:

Hibernate:selectidcard0_.idasid5_0_,idcard0_.authorize_dateasauthorize2_5_0_fromid_cardidcard0_whereidcard0_.id=?

Hibernate:selectperson0_.idasid4_1_,person0_.nameasname4_1_,idcard1_.idasid5_0_,idcard1_.authorize_dateasauthorize2_5_0_fromPersonperson0_leftouterjoinid_cardidcard1_onperson0_.id=idcard1_.idwhereperson0_.id=?

虽然也不实现懒加载功能,一次性将主从对象都查询出来,但此时是经过两次查询才得到结果。

如果修改IdCard.hbm.xml映射文件,增加lazy="proxy",如下所示,与缺省时一样的效果,因为缺省时,lazy是=proxy

Xml代码

1.<one-to-onename="person"constrained="true"lazy="proxy"/>

<one-to-onename="person"constrained="true"lazy="proxy"/>如果修改IdCard.hbm.xml映射文件,如下所示,则lazy(懒加载失效),此时效果如测试(4)。

Xml代码

1.<one-to-onename="person"constrained="true"lazy="proxy"fetch="join"/>

<one-to-onename="person"constrained="true"lazy="proxy"fetch="join"/>

二、唯一外键方式实现一对一

基于外键的one-to-one可以描述为多对一。

hibernate一对一唯一外键关联映射(双向关联Person<---->IdCard)

一对一唯一外键双向关联,需要在另一端(person),添加<one-to-one>标签,指示hibernate如何加载

其关联对象,默认根据主键加载idcard,外键关联映射中,因为两个实体采用的是idcard的外键维护的关系,所以不能指定主键加载idcard,而要根据idcard的外键加载,所以采用如下映射方式:

<one-to-onename="idcard"property-ref="person"/>

IdCard.hbm.xml的映射文件如下:

Xml代码

1.<?xmlversion="1.0"?>

2.<!DOCTYPEhibernate-mappingPUBLIC

3."-//Hibernate/HibernateMappingDTD3.0//EN"

4."http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

5.<hibernate-mappingpackage="com.itcast.hibernate.domain">

6.<classname="IdCard"table="id_card">

7.<idname="id">

8.<generatorclass="native"/>

9.</id>

10.<propertyname="authorizeDate"column="authorize_date"/>

11.<!--指定多的一端的unique=true,这样就限制了多的一端的多重性为一

12.通过这种手段映射一对一唯一外键关联-->

13.<many-to-onename="person"column="person_id"unique="true"/>

14.</class>

15.</hibernate-mapping>

<?xmlversion="1.0"?>

<!DOCTYPEhibernate-mappingPUBLIC

"-//Hibernate/HibernateMappingDTD3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mappingpackage="com.itcast.hibernate.domain">

<classname="IdCard"table="id_card">

<idname="id">

<generatorclass="native"/>

</id>

<propertyname="authorizeDate"column="authorize_date"/>

<!--指定多的一端的unique=true,这样就限制了多的一端的多重性为一

通过这种手段映射一对一唯一外键关联-->

<many-to-onename="person"column="person_id"unique="true"/>

</class>

</hibernate-mapping>Person.hbm.xml的映射文件如下:

Xml代码

1.<?xmlversion="1.0"?>

2.<!DOCTYPEhibernate-mappingPUBLIC

3."-//Hibernate/HibernateMappingDTD3.0//EN"

4."http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

5.<hibernate-mappingpackage="com.itcast.hibernate.domain">

6.<classname="Person">

7.<idname="id">

8.<generatorclass="native"/>

9.</id>

10.<propertyname="name"/>

11.<!--没有下面的one-to-one标签也行,但那样就变成了单向关联(IdCard----》Person),也就是当知道IdCard后,能找到它属于的对应的人,但知道某人后,却无法找到相对应的IdCard-->

12.<one-to-onename="idCard"property-ref="person"/>

13.</class>

14.</hibernate-mapping>

<?xmlversion="1.0"?>

<!DOCTYPEhibernate-mappingPUBLIC

"-//Hibernate/HibernateMappingDTD3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mappingpackage="com.itcast.hibernate.domain">

<classname="Person">

<idname="id">

<generatorclass="native"/>

</id>

<propertyname="name"/>

<!--没有下面的one-to-one标签也行,但那样就变成了单向关联(IdCard----》Person),也就是当知道IdCard后,能找到它属于的对应的人,但知道某人后,却无法找到相对应的IdCard-->

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

</class>

</hibernate-mapping>实体类不用修改,还是用上面的测试类进行测试即可。

保存测试类运行后,相对共享主键方式的one-to-one,id_card表的结构发生了变化,表结构如下所示:

DROPTABLEIFEXISTS`test`.`id_card`;

CREATETABLE`test`.`id_card`(

`id`int(11)NOTNULLAUTO_INCREMENT,

`authorize_date`datetimeDEFAULTNULL,

`person_id`int(11)DEFAULTNULL,

PRIMARYKEY(`id`),

UNIQUEKEY`person_id`(`person_id`),

KEY`FK627C1FB45B253C91`(`person_id`),

CONSTRAINT`FK627C1FB45B253C91`FOREIGNKEY(`person_id`)REFERENCES`person`(`id`)

)ENGINE=InnoDBAUTO_INCREMENT=2DEFAULTCHARSET=utf8;

数据库表中记录如下:

mysql>select*fromperson;

+----+---------+

|id|name|

+----+---------+

|1|person1|

+----+---------+

1rowinset(0.00sec)

mysql>select*fromid_card;

+----+---------------------+-----------+

|id|authorize_date|person_id|

+----+---------------------+-----------+

|1|2010-03-2322:40:38|1|

+----+---------------------+-----------+

1rowinset(0.00sec)

如果Person.hbm.xml映射文件中没有<one-to-one/>这一项的话,运行测试:

Java代码

1.Personperson=(Person)session.get(Person.class,id);

2.System.out.println(person.getIdCard().getAuthorizeDate());

Personperson=(Person)session.get(Person.class,id);

System.out.println(person.getIdCard().getAuthorizeDate());会抛出如下异常:

java.lang.NullPointerException

因为这种关系成了IdCard--->Person的单向关联了。知道了Person,找不到对应的IdCard.

当运行如下测试时:

Java代码

1.Personperson=(Person)session.get(Person.class,id);

2.System.out.println(person.getIdCard());

Personperson=(Person)session.get(Person.class,id);

System.out.println(person.getIdCard());控制台会打印出Person相对应的IdCard为null.

但如果得到了IdCard,却能找到相应的Person.测试如下:

Java代码

1.IdCardidCard=(IdCard)session.get(IdCard.class,id);

2.System.out.println(idCard.getPerson().getName());

IdCardidCard=(IdCard)session.get(IdCard.class,id);

System.out.println(idCard.getPerson().getName());能得到正常的结果,personname为person1.

总结:在缺省情况下,hibernate只有在一对一关联中,查询主对象时,是进行关联查询一次得到查询结果,其它(多对多、多对一、一对多、一对一查询从对象)的查询都是分两次查询得到查询结果。

相关推荐