来源: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类:
- package com.reiyen.hibernate.domain;
- public class Person {
- private int id;
- private String name;
- private IdCard idCard;
- //setter和getter方法
- }
IdCard类:
- package com.reiyen.hibernate.domain;
- public class IdCard {
- private int id;
- private Date authorizeDate;
- private Person person;
- //setter和getter方法
- }
2.映射文件:
Person.hbm.xml文件如下:
- <?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 package="com.reiyen.hibernate.domain">
- <class name="Person" >
- <id name="id" >
- <generator class="native" />
- </id>
- <property name="name" />
- <one-to-one name="idCard" />
- </class>
- </hibernate-mapping>
IdCard.hbm.xml文件如下:
- <?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 package="com.reiyen.hibernate.domain">
- <class name="IdCard" table="id_card">
- <id name="id">
- <!-- id_card的主键来源person,也就是共享idCard的主键 -->
- <generator class="foreign">
- <param name="property">person</param>
- </generator>
- </id>
- <property name="authorizeDate" column="authorize_date" />
- <!-- one-to-one标签的含义,指示hibernate怎么加载它的关联对象,默认根据主键加载,
- constrained="true", 表明当前主键上存在一个约束,id_card的主键作为外键参照了person -->
- <one-to-one name="person" constrained="true"></one-to-one>
- </class>
- </hibernate-mapping>
3. 在hibernate.cfg.xml文件中注册映射文件:
- <mapping resource="com/reiyen/hibernate/domain/Person.hbm.xml" />
- <mapping resource="com/reiyen/hibernate/domain/IdCard.hbm.xml" />
4.测试类如下:
- public class One2One {
- public static void main(String[] args) {
- Person person = add();
- System.out.println("peron name:" + person.getName());
- }
- static Person add(){
- Session session = null;
- Transaction tran = null;
- try {
- session = HibernateUtil.getSession();
- IdCard idCard = new IdCard();
- idCard.setAuthorizeDate(new Date());
- Person p = new Person();
- p.setName("person1");
- p.setIdCard(idCard); // 1
- idCard.setPerson(p); // 2
- tran = session.beginTransaction();
- session.save(p);
- session.save(idCard);
- tran.commit();
- return p;
- } finally {
- if (session != null)
- session.close();
- }
控制台打印信息如下所示:
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)
在测试时一定要注意写上这句代码:
- idCard.setPerson(p);
这是让从对象关联上它所从属的主对象。如果没有这句话,则会抛出如下异常:
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: person
5. 查询测试,测试类如下:
- public class One2One {
- public static void main(String[] args) {
- query(1);
- }
- static void query(Integer id){
- Session session = null;
- try {
- session = HibernateUtil.getSession();
- Person person = (Person)session.get(Person.class, id);//4
- System.out.println(person.getIdCard().getAuthorizeDate());//3
- //IdCard idCard = (IdCard)session.get(IdCard.class, id); //1
- //System.out.println(idCard.getPerson().getName()); //2
- } finally {
- if (session != null)
- session.close();
- }
- }
- }
执行此测试类时,控制台打印信息如下所示:
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",如下所示:
- <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",如下所示:
- <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
- <one-to-one name="person" constrained="true" lazy="proxy" />
如果修改IdCard.hbm.xml映射文件,如下所示,则lazy(懒加载失效),此时效果如测试(4)。
- <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 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 package="com.itcast.hibernate.domain">
- <class name="IdCard" table="id_card">
- <id name="id">
- <generator class="native" />
- </id>
- <property name="authorizeDate" column="authorize_date" />
- <!-- 指定多的一端的unique=true,这样就限制了多的一端的多重性为一
- 通过这种手段映射一对一唯一外键关联 -->
- <many-to-one name="person" column="person_id" unique="true" />
- </class>
- </hibernate-mapping>
Person.hbm.xml的映射文件如下:
- <?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 package="com.itcast.hibernate.domain">
- <class name="Person" >
- <id name="id" >
- <generator class="native" />
- </id>
- <property name="name" />
- <!-- 没有下面的one-to-one标签也行,但那样就变成了单向关联(IdCard ----》 Person) ,也就是当知道IdCard后,能找到它属于的对应的人,但知道某人后,却无法找到相对应的IdCard-->
- <one-to-one name="idCard" property-ref="person"/>
- </class>
- </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/>这一项的话,运行测试:
- Person person = (Person)session.get(Person.class, id);
- System.out.println(person.getIdCard().getAuthorizeDate());
会抛出如下异常:
java.lang.NullPointerException
因为这种关系成了IdCard--->Person的单向关联了。知道了Person,找不到对应的IdCard.
当运行如下测试时:
- Person person = (Person)session.get(Person.class, id);
- System.out.println(person.getIdCard());
控制台会打印出Person相对应的IdCard为null.
但如果得到了IdCard,却能找到相应的Person.测试如下:
- IdCard idCard = (IdCard)session.get(IdCard.class, id);
- System.out.println(idCard.getPerson().getName());
能得到正常的结果,person name为person1.
总结: 在缺省情况下,hibernate只有在一对一关联中,查询主对象时,是进行关联查询一次得到查询结果,其它(多对多、多对一、一对多、一对一查询从对象)的查询都是分两次查询得到查询结果。
相关推荐
本话题主要探讨两种基本的关系映射类型:many-to-one(多对一)和one-to-many(一对多)。这两种关系映射是数据库中常见的关联类型,理解和掌握它们对于开发高质量的Java应用至关重要。 1. many-to-one关系 many-to...
在Hibernate的hbm.xml文件中,一对一关系通过<one-to-one>标签,一对多关系通过、、或标签来定义。需要详细指定实体类名、主键列名以及关联的外键列名。 2. 注解配置: 在实体类上直接使用@OneToOne、@OneToMany等...
在本文中,我们将详细介绍Hibernate一对一唯一外键关联映射的概念、配置方法和实践应用。 一对一唯一外键关联映射的概念 在Hibernate中,一对一唯一外键关联映射是指两个实体之间的关联关系,其中一个实体作为外键...
在Hibernate中,"one-to-one"(一对一)关联是一种常见的关系映射类型,表示两个实体之间存在一对一的对应关系,就像一个人只能有一个护照,或者一辆车只能属于一个人一样。 一、一对一关系的配置 1. **注解配置**...
在Java的持久化框架Hibernate中,一对一(One-to-One)关联是对象关系映射(ORM)中的一个重要概念,它用于表示两个实体之间一对一的映射关系。本篇将深入探讨Hibernate 3.2中的一对一关系,以及如何在实际项目中...
在Hibernate框架中,双向一对多关联映射是常见的对象关系映射(ORM)方式,用于在Java对象模型中表示数据库中的两个实体之间的多对一关系。在这个场景中,"一"端通常指的是一个实体可以拥有多个另一个实体的实例,而...
在Java世界中,Hibernate是一个非常...总结,`Hibernate one-to-many`注解是处理实体间多对一关系的关键工具,它简化了数据库操作,提升了开发效率。正确理解和使用这些注解,对于构建高效、可维护的Java应用至关重要。
在Hibernate中,可以通过 `<many-to-one>` 或者 `<one-to-many>` 标签中的 `cascade` 属性来指定这种行为。 #### 代码示例解析 在提供的代码示例中,`<many-to-one>` 标签中包含了 `cascade` 属性的注释示例,但是...
### Hibernate一对多关系详解 在Hibernate框架中,处理实体间的关系是常见的需求,尤其是在复杂的业务逻辑中。本文将深入探讨Hibernate中一对多(One-to-Many)关系的处理方式,特别是通过外键映射和inverse属性的...
一对一关联可以通过 `<one-to-one>` 标签实现。假设我们有 `Book` 和 `Author` 两个实体类,每个 `Book` 只有一个 `Author`。 ```java public class Author { private Long id; private String name; private ...
在Java的持久化框架Hibernate中,一对一(One-to-One)关系映射是一种常见的实体关联方式。基于外键的One-to-One映射是其中的一种实现方式,它通过在一方或双方实体的表中添加外键来建立关联。接下来,我们将深入...
本示例将详细讲解如何在Hibernate中实现多对多(many-to-many)的关系映射。 在数据库设计中,多对多关系是指两个实体之间存在多个关联,比如学生和课程的关系,一个学生可以选修多门课程,一门课程也可以被多个...
对于`Department`,映射文件中会有一个`<class>`标签,其中包含`<set>`标签来定义员工集合,并且使用`<many-to-many>`或`<one-to-many>`标签来映射与`Employee`的关系。对于`Employee`,我们使用`<class>`标签,并在...
在Java的持久化框架Hibernate中,一对一(One-to-One)关联是对象关系映射(ORM)中的一个重要概念。这种关联关系意味着一个实体最多只能与另一个实体的实例进行关联,反之亦然。本示例将深入讲解如何在Hibernate中...
在Java的持久化框架Hibernate中,一对一(One-to-One)关联映射是常见的关系映射类型之一。它用于表示两个实体之间一对一的关联关系,即一个实体对应另一个实体的唯一实例。下面将详细介绍Hibernate中如何进行一对一...
在深入探讨Hibernate中的一对一(One-to-One)主键双线关联机制之前,我们首先需要理解几个核心概念:Hibernate框架、实体关系映射以及主键关联的基本原理。Hibernate是Java平台下的一款开源对象关系映射(ORM)框架...
本篇将重点探讨Hibernate中的Many-to-One关系映射,帮助开发者更深入地理解和运用这一关键特性。 Many-to-One关系是现实世界中常见的关联类型,一个实体可能对应另一个实体的多个实例,例如,一个员工可以属于一个...
**标题**: Hibernate 一对一实例 ...通过深入研究这些源码,开发者可以更好地理解Hibernate一对一映射的机制,以及如何在实际项目中应用。这个示例是一个很好的起点,可以帮助你掌握这一关键的Hibernate特性。
本文主要关注Hibernate中的一个核心概念——一对一(One-to-One)、一对多(One-to-Many)和多对一(Many-to-One)关联映射,特别是关于“一到多”单向和双向关联映射的配置心得。 首先,让我们了解“一到多”关联...
<one-to-many class="com.example.Employee"/> ``` 在"Employee"实体类的映射文件中,需要定义一个反向引用的引用,即`ManyToOne`类型,指定对应的部门ID字段。 ```xml <!-- Employee.hbm.xml --> <many-to-one ...