hibernate 一对多(one-to-many)双向关联

一对多(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,直接创建了一个集合代理对象的返回给你(把对象引用先给你),等你需要数据的时候再去查询数据库,以提高效率!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值