《Hibernate的延迟加载.docx》由会员分享,可在线阅读,更多相关《Hibernate的延迟加载.docx(7页珍藏版)》请在金锄头文库上搜索。
1、Hibernate_Query_iterate的延迟加载1:通过Query接口的list()方法获取所有对象,然后调用Sesssion.delete(Obj)来删除对象2:通过Query接口的iterate()方法获取Iterator来迭代每个对象分别调用Sesssion.delete(Obj)来删除对象分析:Query.list()方法通过一条select语句来查询整个表来得到所以对象,且获得的对象是完整的.而Query.iterate()方法执行后,发出第一条select语句,只查询表的ID的一列!Entities returned as results are initialized o
2、n demand. The first SQL query returns identifiers only.对于每个对象,通过Sesssion.delete(Obj)来删除对象并,要先获取整个对象,当且仅当此时,才发出SQL语句来查询目标对象(行).只有当你对iterate()中的对象进行操作时,Hibernate才会向数据库再次发送SQL语句来获取该对象属性值.这就是Hibernate的”延迟加载”,” initialized on demand”并延迟加载时,Session不能被关闭,就是说如果Session被关闭了后,延迟加载就会抛出异常: org.hibernate.LazyInit
3、ializationException: could not initialize proxy - no Sessionat org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)at org.hibernate.proxy.pojo.javassist.JavassistLazy
4、Initializer.invoke(JavassistLazyInitializer.java:190)at beans.People_$_javassist_0.toString(People_$_javassist_0.java)1:SRC1/Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();Query query = session.createQuery(from People);Iterator iter = query.iterate();Peopl
5、e people = (People)iter.next();System.out.println(“-“);System.out.println(people);mit();session.close();/执行结果:通过Debug单步运行,我们会发现 查询people的SQL语句 是在System.out.println(people)执行时才发出的!为什么最终查询people的语句是在System.out.println(people)执行时才出发的?分析:(因为Query.iterate()返回的结果是延迟加载的)People people = (People)iter.next();
6、因为people会被延迟到其真正被使用时才加载.所以上面这一句执行后,people还没有被加载的.System.out.println(people);就在这一句才真正用到people,所以此时才会数据库发出SQL语句/2:SRC2/Session session = sessionFactory.openSession();Transaction tx = session.beginTransaction();Query query = session.createQuery(from People);Iterator iter = query.iterate();People people
7、 = (People)iter.next();System.out.println(“-“);mit();session.close();System.out.println(people);/执行结果:程序抛出org.hibernate.LazyInitializationException: could not initialize proxy - no Session 异常!System.out.println(people);执行到这一句时,程序的延迟初始化被执行!但因为此时Session已关闭(JDBC的Connection已被关闭)所以无法发出SQL语句,所以抛出异常!Query.
8、iterate()的延迟初始化深入分析(又是动态代理)在SRC1中, People people = (People)iter.next();这一句执行完后,实际查询people的语句还未发出,但people引用的实例是什么呢?看图: 这个对象中的也有People类的所以字段,看看,还有一个handler字段,就是动态代理了嘛,是javassist直接生成字节码来生成的代理类: People_$_javassist_0调用people.toString()后,流程来到: JavassistLazyInitialize.invoke(proxy,thisMethod,proceed,args)方
9、法此时,thisMethod: public java.lang.String beans.People.toString()proceed: public final java.lang.String beans.People_$_javassist_0._d26toString()public Object invoke(final Object proxy,final Method thisMethod,final Method proceed,final Object args) throws Throwable if ( this.constructed ) Object resul
10、t;try /对要调用的方法进行分类,部分类型的方法直接执行,否则/返回INVOKE_IMPLEMENTATIONresult = this.invoke( thisMethod, args, proxy );catch ( Throwable t ) throw new Exception( t.getCause() );if ( result = INVOKE_IMPLEMENTATION ) /初始化目标:(延迟加载的关键),就在此时才发出SQL语句/第一次访问代理类时,从数据库加载目标对象;/第二次访问代理类或以后时,直接返回Object target = getImplementat
11、ion();final Object returnValue;try if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) if ( !thisMethod.getDeclaringClass().isInstance( target ) ) throw new ClassCastException( target.getClass().getName() );/目标被加载后,调用对应的方法(这里是toString()returnValue = thisMethod.invoke( target, args );else if
12、 ( !thisMethod.isAccessible() ) thisMethod.setAccessible( true );returnValue = thisMethod.invoke( target, args );return returnValue = target ? proxy : returnValue;catch ( InvocationTargetException ite ) throw ite.getTargetException();/如果方法在result = this.invoke( thisMethod, args, proxy );/已执行,直接返回结果e
13、lse return result;else / while constructor is runningif ( thisMethod.getName().equals( getHibernateLazyInitializer ) ) return this;else return proceed.invoke( proxy, args );总结: 原理和JDK的动态代理没什么太多的区别,这可以被称为”延迟加载代理”吧,1,这个代理继承了被代理的类(目标类target)2,当这个代理第一次被访问时,它会先访问数据库来生成目标的实例,然后再调用目标对象上对应的方法.3,当第二次访问这个代理时,因为目标已被加载,所以不用重新加载,直接访问目标上的方法(用反射的Method)一句话概括: 取得的对象实例上是它的代理,当且仅当访问第一次访问这个代理时,它才会加载被代理的目标