转自:http://ryxxlong.iteye.com/blog/622652

=================

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

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

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

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

1. 实体类设计如下:

Person类:

Java代码  
  1. package com.reiyen.hibernate.domain;
  2. public class Person {
  3. private int id;
  4. private String name;
  5. private IdCard idCard;
  6. //setter和getter方法
  7. }

IdCard类:

Java代码  
  1. package com.reiyen.hibernate.domain;
  2. public class IdCard {
  3. private int id;
  4. private Date authorizeDate;
  5. private Person person;
  6. //setter和getter方法
  7. }

2.映射文件:

Person.hbm.xml文件如下:

Xml代码  
  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">
  6. <class name="Person" >
  7. <id name="id" >
  8. <generator class="native" />
  9. </id>
  10. <property name="name" />
  11. <one-to-one name="idCard" />
  12. </class>
  13. </hibernate-mapping>

IdCard.hbm.xml文件如下:

Xml代码  
  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">
  6. <class name="IdCard" table="id_card">
  7. <id name="id">
  8. <!-- id_card的主键来源person,也就是共享idCard的主键 -->
  9. <generator class="foreign">
  10. <param name="property">person</param>
  11. </generator>
  12. </id>
  13. <property name="authorizeDate" column="authorize_date" />
  14. <!-- one-to-one标签的含义,指示hibernate怎么加载它的关联对象,默认根据主键加载,
  15. constrained="true", 表明当前主键上存在一个约束,id_card的主键作为外键参照了person -->
  16. <one-to-one name="person" constrained="true"></one-to-one>
  17. </class>
  18. </hibernate-mapping>

3. 在hibernate.cfg.xml文件中注册映射文件:

Xml代码  
  1. <mapping resource="com/reiyen/hibernate/domain/Person.hbm.xml" />
  2. <mapping resource="com/reiyen/hibernate/domain/IdCard.hbm.xml" />

4.测试类如下:

Java代码  
  1. public class One2One {
  2. public static void main(String[] args) {
  3. Person person = add();
  4. System.out.println("peron name:" + person.getName());
  5. }
  6. static Person add(){
  7. Session session = null;
  8. Transaction tran = null;
  9. try {
  10. session = HibernateUtil.getSession();
  11. IdCard idCard = new IdCard();
  12. idCard.setAuthorizeDate(new Date());
  13. Person p = new Person();
  14. p.setName("person1");
  15. p.setIdCard(idCard);   // 1
  16. idCard.setPerson(p);   // 2
  17. tran = session.beginTransaction();
  18. session.save(p);
  19. session.save(idCard);
  20. tran.commit();
  21. return p;
  22. } finally {
  23. if (session != null)
  24. session.close();
  25. }

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

Hibernate: insert into Person (name) values (?)
Hibernate: insert into id_card (authorize_date, id) values (?, ?)
person name : person1

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

DROP TABLE IF EXISTS `test`.`id_card`;
CREATE TABLE  `test`.`id_card` (
  `id` int(11) NOT NULL,
  `authorize_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK627C1FB4284AAF67` (`id`),
  CONSTRAINT `FK627C1FB4284AAF67` FOREIGN KEY (`id`) REFERENCES `person` (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据库中记录如下所示:
mysql> select * from person;
+----+---------+
| id | name    |
+----+---------+
|  1 | person1 |
+----+---------+
1 row in set (0.00 sec)

mysql> select * from id_card;
+----+---------------------+
| id | authorize_date      |
+----+---------------------+
|  1 | 2010-03-23 01:07:25 |
+----+---------------------+
1 row in set (0.00 sec)

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

Java代码  
  1. idCard.setPerson(p);

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

org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: person

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

Java代码  
  1. public class One2One {
  2. public static void main(String[] args) {
  3. );
  4. }
  5. static void query(Integer id){
  6. Session session = null;
  7. try {
  8. session = HibernateUtil.getSession();
  9. Person person = (Person)session.get(Person.class, id);//4
  10. System.out.println(person.getIdCard().getAuthorizeDate());//3
  11. //IdCard idCard = (IdCard)session.get(IdCard.class, id); //1
  12. //System.out.println(idCard.getPerson().getName());      //2
  13. } finally {
  14. if (session != null)
  15. session.close();
  16. }
  17. }
  18. }

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

Hibernate: select person0_.id as id3_1_, person0_.name as name3_1_, idcard1_.id as id4_0_, idcard1_.authorize_date as authorize2_4_0_ from Person person0_ left outer join id_card idcard1_ onperson0_.id=idcard1_.id where
person0_.id=?
2010-03-23 21:46:07.0

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

Hibernate: select idcard0_.id as id4_0_, idcard0_.authorize_date as authorize2_4_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id3_1_, person0_.name as name3_1_, idcard1_.id as id4_0_, idcard1_.authorize_date as authorize2_4_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
person1

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

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

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

必须同时满足下面的三个条件时才能实现懒散加载:1).lazy!=false (lazy缺省方式就!=false)2).constrained=true 3).fetch=select(fetch缺省方式即为select)
(因为主表不能有constrained=true,所以主表没有懒加载功能)。能够懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理对象)的属性(getId和getClass除外)时,hibernate会初始化这些代理,或用Hibernate.initialize(proxy)来初始化代理对象;当相关联的session关闭后,再访问懒加载的对象将会出现异常。

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

Hibernate: select person0_.id as id4_1_, person0_.name as name4_1_, idcard1_.id as id5_0_, idcard1_.authorize_date as authorize2_5_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id
where person0_.id=?

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

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

Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?

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

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

Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id4_1_, person0_.name as name4_1_, idcard1_.id as id5_0_, idcard1_.authorize_date as authorize2_5_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
person1

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

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

Xml代码  
  1. <one-to-one name="person" constrained="true" fetch="join"/>

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

Hibernate: select idcard0_.id as id5_1_, idcard0_.authorize_date as authorize2_5_1_, person1_.id as id4_0_, person1_.name as name4_0_ from id_card idcard0_ inner join Person person1_
on idcard0_.id=person1_.id where idcard0_.id=?
此时查询从对象IdCard时也不再懒加载了,通过inner join一次性将主从对象都查询出来。

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

Xml代码  
  1. <one-to-one name="person" constrained="true" lazy="false" />

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

Hibernate: select idcard0_.id as id5_0_, idcard0_.authorize_date as authorize2_5_0_ from id_card idcard0_ where idcard0_.id=?
Hibernate: select person0_.id as id4_1_, person0_.name as name4_1_, idcard1_.id as id5_0_, idcard1_.authorize_date as authorize2_5_0_ from Person person0_ left outer join id_card idcard1_ on person0_.id=idcard1_.id where person0_.id=?
虽然也不实现懒加载功能,一次性将主从对象都查询出来,但此时是经过两次查询才得到结果。

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

Xml代码  
  1. <one-to-one name="person" constrained="true" lazy="proxy" />

如果修改IdCard.hbm.xml映射文件,如下所示,则lazy(懒加载失效),此时效果如测试(4)。

Xml代码  
  1. <one-to-one name="person" constrained="true" lazy="proxy" fetch="join"/>

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

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

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

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

Xml代码  
  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.itcast.hibernate.domain">
  6. <class name="IdCard" table="id_card">
  7. <id name="id">
  8. <generator class="native" />
  9. </id>
  10. <property name="authorizeDate" column="authorize_date" />
  11. <!-- 指定多的一端的unique=true,这样就限制了多的一端的多重性为一
  12. 通过这种手段映射一对一唯一外键关联 -->
  13. <many-to-one name="person" column="person_id" unique="true" />
  14. </class>
  15. </hibernate-mapping>

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

Xml代码  
  1. <?xml version="1.0"?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mapping package="com.itcast.hibernate.domain">
  6. <class name="Person" >
  7. <id name="id" >
  8. <generator class="native" />
  9. </id>
  10. <property name="name" />
  11. <!-- 没有下面的one-to-one标签也行,但那样就变成了单向关联(IdCard ----》 Person) ,也就是当知道IdCard后,能找到它属于的对应的人,但知道某人后,却无法找到相对应的IdCard-->
  12. <one-to-one name="idCard" property-ref="person"/>
  13. </class>
  14. </hibernate-mapping>

实体类不用修改,还是用上面的测试类进行测试即可。

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

DROP TABLE IF EXISTS `test`.`id_card`;
CREATE TABLE  `test`.`id_card` (
  `id` int(11) NOT NULL AUTO_INCREMENT, 
  `authorize_date` datetime DEFAULT NULL,
  `person_id` int(11) DEFAULT NULL, 
  PRIMARY KEY (`id`),
  UNIQUE KEY `person_id` (`person_id`) ,
  KEY `FK627C1FB45B253C91` (`person_id`),
  CONSTRAINT `FK627C1FB45B253C91` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

数据库表中记录如下:

mysql> select * from person;
+----+---------+
| id | name    |
+----+---------+
|  1 | person1 |
+----+---------+
1 row in set (0.00 sec)

mysql> select * from id_card;
+----+---------------------+-----------+
| id | authorize_date      | person_id |
+----+---------------------+-----------+
|  1 | 2010-03-23 22:40:38 |         1 |
+----+---------------------+-----------+
1 row in set (0.00 sec)

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

Java代码  
  1. Person person = (Person)session.get(Person.class, id);
  2. System.out.println(person.getIdCard().getAuthorizeDate());

会抛出如下异常:

java.lang.NullPointerException

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

当运行如下测试时:

Java代码  
  1. Person person = (Person)session.get(Person.class, id);
  2. System.out.println(person.getIdCard());

控制台会打印出Person相对应的IdCard为null.

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

Java代码  
  1. IdCard idCard = (IdCard)session.get(IdCard.class, id);
  2. System.out.println(idCard.getPerson().getName());

能得到正常的结果,person name为person1.

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

Hibernate笔记——(ONE TO ONE)一对一的更多相关文章

  1. 框架Hibernate笔记系列 基础Session

    标题:框架Hibernate笔记 资料地址: 1. www.icoolxue.com 孔浩 1.背景简介 Hibenate是JBoss公司的产品.它是数据持久化的框架.Usually,我们使用JDBC ...

  2. [原创]java WEB学习笔记84:Hibernate学习之路-- -映射 一对一关系 ,基外键的方式实现

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  3. hibernate笔记--基于主键的单(双)向的一对一映射关系

    上一节介绍的基于外键的一对一映射关系中,在Person表中有一个外键列idCard_id,对应的idCard表的主键id,至于基于主键的一对一映射关系,就是指Person表中抛弃了idcard_id这 ...

  4. hibernate笔记--基于外键的单(双)向的一对一映射关系

    假设我们有两张表,人员信息表Person,和身份信息表IdCard,我们知道每个人只有一个身份证号,所以这里的Person和IdCard表是一一对应的,也就是一对一的映射关系,基于外键的单向一对一映射 ...

  5. [原创]java WEB学习笔记85:Hibernate学习之路-- -映射 一对一关系 ,基于主键方式实现

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  6. Hibernate笔记——关联关系配置(一对多、一对一和多对多)

    原文:http://www.cnblogs.com/otomedaybreak/archive/2012/01/20/2327695.html ============================ ...

  7. Hibernate各种基本注解及一对一(多)关系映射采坑笔记

    hibernate提供两种方式配置关系映射,一种XMl配置,一种注解.SpringBoot已经自带了hibernate注解方式,我也是特别喜欢使用注解,特此记下常用的知识点. 1.基本注解 @Tabl ...

  8. Hibernate笔记一

    背景 jdbc的优缺点 A:直接操作底层,提供了简单,便捷的访问数据库方法,跨平台比较强,灵活,可以写很多赋值的SQL语句:是最底层的数据库操作,所以效率比较高,Sql语句可以自己选择写,采用效率最高 ...

  9. hibernate笔记--cascade级联以及inverse属性

    cascade : 不管是单向多对一还是一对多,或者是双向的一对多关系,在一的一端映射文件中有一个set标签,在多的一端有many-to-one标签,拿前几篇笔记里讲的Grade和Student举例, ...

随机推荐

  1. Java团队项目总结

    Java团队项目总结 1.项目实现情况 项目概述: 我们团队项目准备实现一个有关于大富翁有的游戏程序. 大富翁游戏,以经营权为主要的游戏方式,通过购买经营权与架构经营的星级服务来获得最大的利益,当其他 ...

  2. UITableView的常用属性和cell的内存优化

    UITableView的常用属性: 分割线颜色设置: 1> 设置separatorStyle: 分割线的颜色 方法:tableView.separatorStyle = UITableViewC ...

  3. mysql 存储过程 -- 游标的使用(备忘)

    BEGIN ; DECLARE f_ratio FLOAT DEFAULT 0.8; ); ); DECLARE i_statDate DATE; DECLARE i_accumulateCount ...

  4. 【 Regular Expression Matching 】cpp

    题目: Implement regular expression matching with support for '.' and '*'. '.' Matches any single chara ...

  5. 服务器NPC的ID如何分配的

    服务器ID分配包括NPC,Monster,Pet的ID分配都是调用allocateUID然后自动保存的ID加一,pet说是通过玩家的ID移位获得的,调试一下发现还是调用allocateUID,如果通过 ...

  6. Connection reset by peer: socket write error 连数据库出现改错

    1.网络原因 2.从池中获取连接后没有释放到池中导致的

  7. [geeksforgeeks] Bottom View of a Binary Tree

    http://www.geeksforgeeks.org/bottom-view-binary-tree/ Bottom View of a Binary Tree Given a Binary Tr ...

  8. Python中的元类和__metaclass__

    1.什么是元类 元类让你来定义某些类是如何被创建的,从根本上说,赋予你如何创建类的控制权.可以把元类想成是一个类中类,或是一个类,它的实例是其它的类.当某个类调用type()函数时,你就会看到它到底是 ...

  9. IE8下jQuery改变png图片透明度时出现的黑边问题

    png24格式的图片在用jQuery添加显示隐藏动画时发现,图片的半透明区域出现黑边? 在网上搜了搜主要有以下几种办法: 1.把图片保存成PNG-8格式. 2.把背景色一起切入并保存为JPG格式. 以 ...

  10. Create Script Template In Edit Mode

    很多时候 许多类 的 格式 都是重复的,比如 从配置文件中映射出来的类. 这个时候写一个 类模板 就很节省时间了. Code public static string TestPath = " ...