转:http://ryxxlong.iteye.com/blog/621753
一对多(one-to-many)双向关联实例(Department- Employee)
这里的一对多双向关联是在域模型(实体对象模型)上的概念,在关系数据库中,只存在外键参照关系,而且总是由"many"方参照"one"方,因为这样才能消除冗余数据,因上关系数据库实际上只支持多对一或一对一的单向关联.在实体(类与类之间)各种各样的关系中,数多对一的的单向关联关系与数据库中的外键参照关系最匹配了.
在我的前一篇文章多对一的基础上进行修改。
1.修改Department实体类:
- package com.reiyen.hibernate.domain
- public class Department {
- private int id;
- private String name;
- private Set
emps; - //setter和getter方法
- }
2.修改Department.hbm.xml文件:
- xml version="1.0"?>
- "-//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="Department" >
- <id name="id" >
- <generator class="native" />
- id>
- <property name="name" />
- <set name="emps">
- <key column="depart_id" />
- <one-to-many class="Employee"/>
- set>
- class>
- hibernate-mapping>
Hibernate根椐以上配置信息获得如下信息:
3. 其余的保持原样,测试类如下:
- public class Many2One {
- /**
- * @param args
- */
- public static void main(String[] args) {
- Department depart = add();
- Department department = queryDepart(depart.getId());
- //department.getEmps();3
- // System.out.println("emp size:" + department.getEmps().size());4
- }
- static Department queryDepart(int departId) {
- Session s = null;
- Transaction tx = null;
- try {
- s = HibernateUtil.getSession();
- tx = s.beginTransaction();
- Department depart = (Department) s.get(Department.class, departId);
- System.out.println("emp size:" + depart.getEmps().size());//1
- // System.out.println("emps class:" + depart.getEmps().getClass()); //2
- // Hibernate.initialize(depart.getEmps());
- tx.commit();
- return depart;
- } finally {
- if (s != null)
- s.close();
- }
- }
- static Department add() {
- Session s = null;
- Transaction tx = null;
- try {
- Department depart = new Department();
- depart.setName("department name");
- Employee employee1 = new Employee();
- employee1.setDepartment(depart); // 对象模型:建立两个对象的关联
- employee1.setName("employee1 name2");
- Employee employee2 = new Employee();
- employee2.setDepartment(depart); // 对象模型:建立两个对象的关联
- employee2.setName("employee2 name2");
- // Set
set = new HashSet (); - // set.add(employee1);
- // set.add(employee2);
- // depart.setEmps(set);
- s = HibernateUtil.getSession();
- tx = s.beginTransaction();
- s.save(depart);
- s.save(employee1);
- s.save(employee2);
- tx.commit();
- return depart;
- } finally {
- if (s != null)
- s.close();
- }
- }
- }
控制台打印信息如下:
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: select department0_.id as id1_0_, department0_.name as name1_0_ from Department department0_ where department0_.id=?
Hibernate: select emps0_.depart_id as depart3_1_, emps0_.id as id1_, emps0_.id as id2_0_, emps0_.name as name2_0_, emps0_.depart_id as depart3_2_0_ from Employee emps0_ where emps0_.depart_id=?
emp size:2
数据库中记录如下所示:
mysql> select * from employee;
+----+-----------------+-----------+
| id | name | depart_id |
+----+-----------------+-----------+
| 1 | employee1 name2 | 1 |
| 2 | employee2 name2 | 1 |
+----+-----------------+-----------+
2 rows in set (0.00 sec)
mysql> select * from department;
+----+-----------------+
| id | name |
+----+-----------------+
| 1 | department name |
+----+-----------------+
1 row in set (0.00 sec)
虽然对象模型修改了,但数据库关系模型并没有改变。
如果把测试程序中注释部分去掉,同时将注释为“建立对象模型”这两句程序注释掉,虽然也能正确运行,但控制台会多打印出两条更新语句,说明数据库多执行了两次更新操作,效率上有影响,如下所示:
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?
Hibernate: select department0_.id as id1_0_, department0_.name as name1_0_ from Department department0_ where department0_.id=?
Hibernate: select emps0_.depart_id as depart3_1_, emps0_.id as id1_, emps0_.id as id2_0_, emps0_.name as name2_0_, emps0_.depart_id as depart3_2_0_ from Employee emps0_ where emps0_.depart_id=?
emp size:2
4.懒加载分析:
(1)如果将程序中标记为1的程序注释掉,控制台打印信息如下:
Hibernate: select department0_.id as id0_0_, department0_.name as name0_0_ from Department department0_ where department0_.id=?
此时只是将Department对象查询出来了,而对应的Employee并没有进行查询。
(2)如果将程序中标记为1的程序注释掉,同时将标记为2的前面的注释去掉,运行程序,控制台打印信息如下:
Hibernate: select department0_.id as id0_0_, department0_.name as name0_0_ from Department department0_ where department0_.id=?
emps class:class org.hibernate.collection.PersistentSet
此时可以看到其实Hibernate将hashset替换成了它自己写的PersistentSet,所以才能实现懒加载功能。同时可以发现当调用department.getEmps();hibernate只是获取了集合代理对象的引用,它并没有去查询数据库来填充集合对象。你还可以进一步测试来验证这个问题。如果你 将程序中标记为1的程序注释掉,同时去掉标记为3的语句前面的注释,你会发现程序能正常运行,但如果去掉标记为4的语句前面的注释,运行程序,则会抛出如下异常:
org.hibernate.LazyInitializationException : failed to lazily initialize a collection of role: com.reiyen.hibernate.domain.Department.emps, no session or session was closed
(3)当进行department对象查询时,从department的表结构中也不能判断这个Department对象是否有对应的Employee,那为什么它不像一对一查询时,也一次性把相关联的对象查询出来呢??这是因为如果将关联的对象查询出来的话,因为数据较多,它会严重影响效率,而不像一对一时,反正从对象只有一个,一次性查询出来影响也不是太大,所以hibernate就假定你有相对应的Employee,直接创建了一个集合代理对象的返回给你(把对象引用先给你),等你需要数据的时候再去查询数据库,以提高效率!