一对多(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<Employee> emps;
//setter和getter方法
}
2.修改Department.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="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>
<set>元素的属性name:设定待映射的持久化类的属性名,这里为Depatment类的emps属性.
<set>元素的两个子元素:
<key>:设定与所关联的持久化类对应的表的外键,此处为Employee表的depart_id字段.
<one-to-many>:设定所关联的持久化类,此处为Employee.
Hibernate根椐以上配置信息获得如下信息:
<set>元素表明Department类的emps属性为java.util.Set集合类型.
<class>子元素表明emps集合中存放的是一组Employee对象
<key>子元素表明employee表是通过外键depart_id参照department表的.
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<Employee> set = new HashSet<Employee>();
// 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,直接创建了一个集合代理对象的返回给你(把对象引用先给你),等你需要数据的时候再去查询数据库,以提高效率!