第十三章 lazy懒加载

lazy属性:懒加载
作用:当调用方法的时候才去执行要执行的SQL语句.
我们以班级学生为示例:
请看班级的配置文件MyClass.hbm.xml
<hibernate-mapping>
<class name="chapter9.model.MyClass" table="myclass">
<id name="id" type="java.lang.String" column="id" length="32">
<generator class="uuid.hex" />
</id>
<property name="name" type="java.lang.String" column="name"
length="20" />
<set name="students">
<key column="student_id" />
<one-to-many class="chapter9.model.Student" />
</set>
</class>
</hibernate-mapping>

重点要看这里<set name="students">,班级对象持久一个学生集合,我们用以下测试代码:
public class Test {
public static void main(String[] args) throws Exception {
MyClassDao myClassDao = new MyClassDao();
StudentDao studentDao = new StudentDao();

MyClass myClass = myClassDao.findById("4028810027d8be080127d8be0d6a0001");
Thread.sleep(5000);
Set<Student> students = myClass.getStudents();
for(Student s:students){
System.out.println(s.getName());
}
}
}
Thread.sleep(5000);是让程序暂停5秒钟,
当我们执行程序以后,可以看到出错了:

[img]http://dl.iteye.com/upload/attachment/243919/0f20b1c3-336a-3f13-9dce-2b6615f72eea.bmp[/img]


但是我们看到程序执行了一条查询语句
select myclass0_.id as id0_0_, myclass0_.name as name0_0_ from myclass myclass0_ where myclass0_.id=?

这一条查询语句对应的是
MyClass myClass = myClassDao.findById("4028810027d8be080127d8be0d6a0001");

可以看到程序正常执行的默认行为是:
当你执行到Set<Student> students = myClass.getStudents();这一句时,也就是当你调用getStudents();方法时,程序才会去再一次查询数据库取出学生集合,但是当程序去数据库查询取学生信息的时候,却发现hibernate关闭了session,所以报了一个错误:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: chapter1.model.MyClass.students, no session or session was closed
no session or session was closed意思是指session对象已经关闭了.

那么怎么解决这个问题呢?
这个问题一直是使用Hibernate的一个缺陷,一直到现在都没有一个很好的解决方案.后面在Spring中会有一个OpenSessionInView解决方案.但也会有很多缺陷.

暂时的解决方案,
 不懒加载
我们只需要给set标签添加一个属性即可.
<set name="students" lazy="false">
<key>
<column name="student_id" length="32" />
</key>
<one-to-many class="chapter1.model.Student" />
</set>
lazy="false"的意思是取消懒加载的行为,那么我们再执行一下刚才的代码,可以看到以下结果:

[img]http://dl.iteye.com/upload/attachment/243917/10d45bea-6bb8-34d0-9468-b7fe0b11b900.bmp[/img]


程序是执行成功了,但是当执行myClassDao.findById("4028810027d8be080127d8be0d6a0001");这一句代码的时候,可以看到程序执行了两条查询语句,程序已经将关联的学生集合取出来了,这就违背了我们的初衷.

 解决方案二:传参数决定是否懒加载
解决懒加载的问题要记住一条,就是要把有用的数据在session关闭之前将数据取出来.
配置文件改为:
<set name="students" lazy="true">
<key>
<column name="student_id" length="32" />
</key>
<one-to-many class="chapter1.model.Student" />
</set>
lazy="true"指定了当程序调用方法时才去执行SQL语句取值,
修改Dao层
transaction.begin();
myClass = (MyClass) session.get(MyClass.class, id);
myClass.getStudents();
transaction.commit();
可以看到myClass.getStudents();这句代码,感觉有点奇怪,就只是调用了一下获取学生集合的方法,但是当你调用这个方法的时候,学生数据就被取出来了.不过这样的写法会产生二异性,就这么放在那里,如果换了其他人看你的代码,可能不知道作用是什么,可能后面维护你代码的人就将这句代码注释掉了,但是一注释就会出问题了,学生集合取不出来了.

Hibernate类提供了一个静态方法来修饰这个作用,将代码改为下面:
transaction.begin();
myClass = (MyClass) session.get(MyClass.class, id);
Hibernate.initialize(myClass.getStudents());
transaction.commit();
这样写了以后,后面维护你代码的人就能够看懂这句话的作用了.

但是现在还是和lazy="false"时的作用一样,如果我后面不需要用到学生集合怎么办,因为这个时候不管你用不用,学生集合都被取出来了.

那如果我现在有时候想取出学生集合,有时候又不想取出学生集合怎么办呢,不可能硬性的将lazy="false",如果这样,就有点不合理了,我们可以给方法加一个boolean值的参数,来决定是否取出学生集合.
将Dao层改为:
public MyClass findById(Serializable id, boolean isLazy) throws Exception {
Session session = null;
Transaction transaction = null;
MyClass myClass = null;
try {
session = HibernateUtil.getSession();
transaction = session.getTransaction();
transaction.begin();
myClass = (MyClass) session.get(MyClass.class, id);
if(isLazy){
Hibernate.initialize(myClass.getStudents());
}
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
}
return myClass;
}
这个时候我们就可以控制是否懒加载了,需要学生集合就取,不需要就不取.非常灵活,但是给方法增加了一点复杂度.记住这时lazy="true"

那么在main方法中调用时就得多传一个参数了.
MyClass myClass = myClassDao.findById(
"4028810027d8be080127d8be0d6a0001", true);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值