Hibernate延迟加载机制收藏
所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以提高程序的性能。
延迟加载一般有三种类型:
1. 实体对象的延迟加载:
在×.hbm.xml文件中的配置如下:
……
通过将class的lazy属性设置为true,来开启延迟加载特性。
调用代码如下:
User user=(User)session.load(User.class,”1”);(1)
System.out.println(user.getName());(2)
当程序运行到(1)的时候,Hibernate并没有查询相应的数据,只有当程序执行到(2)的时候,hibernate才会发起数据库查询操作。
hibernate是通过中间代理对象来实现延迟加载的,因为session.load() 返回的是一个实体对象的代理类,这里所返回的就是User对象的代理类。代理类中包含目标对象的所有属性和方法,并且属性值被赋值为空,当执行getname() 方法的时候,实际上是通过调用代理对象的getname方法来得到值,然后代理对象再去查询数据库。
2. 集合类型的延迟加载
。hbm。xml的配置如下;
…..
通过将
User user=(User)session.load(User.class,”1”);
Collection addset=user.getAddresses(); (1)
Iterator it=addset.iterator(); (2)
while(it.hasNext()){
Address address=(Address)it.next();
System.out.println(address.getAddress());
}
当程序执行到(1)处时,这时并不会发起对关联数据的查询来加载关联数据,只有运行到(2)处时,真正的数据读取操作才会开始,这时Hibernate会根据缓存中符合条件的数据索引,来查找符合条件的实体对象。
在Hibernate中对集合类型进行缓存时,是分两部分进行缓存的,首先缓存集合中所有实体的id列表,然后缓存实体对象,这些实体对象的id列表,就是所谓的数据索引。当查找数据索引时,如果没有找到对应的数据索引,这时就会一条select SQL的执行,获得符合条件的数据,并构造实体对象集合和数据索引,然后返回实体对象的集合,并且将实体对象和数据索引纳入Hibernate的缓存之中。另一方面,如果找到对应的数据索引,则从数据索引中取出id列表,然后根据id在缓存中查找对应的实体,如果找到就从缓存中返回,如果没有找到,在发起select SQL查询。在这里我们看出了另外一个问题,这个问题可能会对性能产生影响,这就是集合类型的缓存策略。如果我们如下配置集合类型:
…..
这里我们应用了
User user=(User)session.load(User.class,”1”);
Collection addset=user.getAddresses();
Iterator it=addset.iterator();
while(it.hasNext()){
Address address=(Address)it.next();
System.out.println(address.getAddress());
}
System.out.println(“Second query……”);
User user2=(User)session.load(User.class,”1”);
Collection it2=user2.getAddresses();
while(it2.hasNext()){
Address address2=(Address)it2.next();
System.out.println(address2.getAddress());
}
运行这段代码,会得到类似下面的输出:
Select * from user where id=’1’;
Select * from address where user_id=’1’;
Tianjin
Dalian
Second query……
Select * from address where id=’1’;
Select * from address where id=’2’;
Tianjin
Dalian
我们看到,当第二次执行查询时,执行了两条对address表的查询操作,为什么会这样?这是因为当第一次加载实体后,根据集合类型缓存策略的配置,只对集合数据索引进行了缓存,而并没有对集合中的实体对象进行缓存,所以在第二次再次加载实体时,Hibernate找到了对应实体的数据索引,但是根据数据索引,却无法在缓存中找到对应的实体,所以Hibernate根据找到的数据索引发起了两条select SQL的查询操作,这里造成了对性能的浪费,怎样才能避免这种情况呢?我们必须对集合类型中的实体也指定缓存策略,所以我们要如下对集合类型进行配置:
…..
此时Hibernate会对集合类型中的实体也进行缓存,如果根据这个配置再次运行上面的代码,将会得到类似如下的输出:
Select * from user where id=’1’;
Select * from address where user_id=’1’;
Tianjin
Dalian
Second query……
Tianjin
Dalian
这时将不会再有根据数据索引进行查询的SQL语句,因为此时可以直接从缓存中获得集合类型中存放的实体对象。
3. 属性的延迟加载
如下配置我们的实体类:
……
通过对
根据上面的配置,执行代码如下:
String sql=”from User user where user.name=’zx’ ”;
Query query=session.createQuery(sql); (1)
List list=query.list();
for(int i=0;i
User user=(User)list.get(i);
System.out.println(user.getName());
System.out.println(user.getResume()); (2)
}
当执行到(1)处时,会生成类似如下的SQL语句:
Select id,age,name from user where name=’zx’;
这时Hibernate会检索User实体中所有非延迟加载属性对应的字段数据,当执行到(2)处时,会生成类似如下的SQL语句:
Select resume from user where id=’1’;
这时会发起对resume字段数据真正的读取操作。
延时加载就是说当你真的要去取对象里的某个属性了才去库里取出来。否则不会真的取出数据来。比如:
User user = getUserById(1);//这个方法通过id查到了一条uid为1的记录,如果你现在没有显式的去取user里的某个值,而是把他返回给业务层,并关闭了session,那么业务层再用user.getName()是会报错的。
如果你的延时加载设置为false的话,你这样查询后返回给业务层,即使session关闭,这个user里面也保存了所有uid为1的信息
一直在用myeclipse写ssh项目,不用你自己去配置ant.都是他生成好的,直接用就可以了。
我就照你说的做了一遍,怎么没有发现问题,确实是延迟加载了啊
映射文件:User.hbm.xml
实体类:
package entity;
public class User {
private int id;
private String username;
private String content;
setters,getters...
}
build.xml文件:
测试类:Test.java
package test;
public class Test {
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
List
for (User user : list) {
System.out.println(user.getId());
System.out.println(user.getUsername());
System.out.println(user.getContent());
}
...
}
}
运行结果(SQL):
Hibernate:
select
user0_.userid as userid0_,
user0_.username as username0_
from
TUser user0_
...
Hibernate:
select
user_.content as content0_
from
TUser user_
where
user_.userid=?
...
lazy延迟加载是适用于某个表中通过外键关联到另一张表,就类似与
class Student{
private Classes classes; //属于某个班级
}
这种情况下.用lazy来实现是否延迟加载Classes这个表.是否能够延迟加载时通过对应的hbm文件来设置的,比如你要让Classes这个表能延迟加载,是需要修改Classes的hbm文件来使Classes能够lazy,而不是设置Student里的属性是否lazy.
再说.你延迟加载String之类的属性干嘛?