hibernate之延迟加载(Lazy Loading)
避免在某些情况下,关联关系给我们带来无谓的开销,Hibernate引入了延迟加载的概念;
Hibernate3中的延迟记载可以针对:
- 实体对象
- 集合(Collection)
- 属性的延迟加载
通过Load方法,可以返回目标实体对象的代理;
1,针对实体对象的延迟加载:
在class标签里添加lazy="true",属性:
<class name="TUser" table="t_user" lazy="true">
如果将lazy="false"时,当程序运行:
TUser user = (TUser) session.load(TUser.class, new Integer(1));
时,会发出SQL,从数据库中取出所对应的记录,并构造了一个完整的TUser对象;
如果将lazy="true"后,程序执行到:
TUser user = (TUser) session.load(TUser.class, new Integer(1));
时,并没有任何SQL语句发出,此时对象里的属性也为空,当程序执行到:
System.out.println("user Name:"+users.getName());
时,可以用Eclipse的Debug看到对象的属性仍然为空,但是看日志输出,会发现已经发出SQL,并也取到了其想要的name名字;
延迟加载和非延迟加载的差异在于Hibernate的代理机制,Hibernate引入了CGLib作为代理机制的实现,也就是我们在DeBug下可以看到TUser$EnhancerByCGLIB$$bede8986类型对象的原因;
CGLIB可以在运行期生成 java Class,这里的代理机制,其基本原理就是通过由CGLIB构造一个包含目标对象所有属性和方法的动态对象(相当于动态构造目标对象的一个子类)返回,并以之作为中介,为目标对象提供更多的特性;
这时,真正的TUser对象,位于代理类的CGLIB$CALLBACK_0.target属性中,当我们调用getName()方法时,实际上调用的是CGLIB$CALLBACK_0.getName()方法,当调用了CGLIB$CALLBACK_0.getName()后,首先会检查 CGLIB$CALLBACK_0.target中是否存在目标对象;如果存在,则调用目标对象的getName方法返回,如果目标对象为空,则发起数据库查询指令,读取记录,构建目标对象并将其设入CGLIB$CALLBACK_0.target。这样,通过一个中间代理,实现数据延迟加载功能,只有当用户真正的调用实体类的取值方法,Hibernate才会去数据库中取值;
2,类型集合的延迟加载
假如我们有个TUser实体对象,这个对象里面有Set<Book> books;属性;在我们获取TUser基本信息的时候,不必要将books一起查出来,这样的话可以给<set />标签添加lazy属性;
<set name="books" cascade="all" lazy="true">
这样的话,只有我们实际中真正的用到book了,才会去数据库里查询!
3,属性延迟加载
现将其属性设置:
<property name="name" lazy="true"/>
配置了lazy属性之外,还要借助类增强器对二进制Class文件进行强化处理(buildtime bytecode instrumentation)。通过ANT调用Hibernate类增强器对TUser.class文件进行强化处理。脚本如下:
<project name="HibernateSample" default="instrument" basedir="."> <property name="lib.dir" value="./lib"/> <property name="classes.dir" value="./bin"/> <path id="lib.class.path"> <fileset dir="${lib.dir}"> <include name="**/*.jar"/> </fileset> <target name="instrument"> <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask"> <classpath path="${classes.dir}"/> <classpath refid="lib.class.path"/> </taskdef> <instrument verbose="true"> <fileset dir="${classes.dir}/com.redsaga/hibernate/db/entity"> <include name="TUser.class"/> </fileset> </instrument> </target> </project>