Hibernate中什么是懒加载异常?该怎么处理懒加载异常?

来源:互联网 发布:空间怎么绑定域名 编辑:程序博客网 时间:2024/04/27 16:42

首先看一下什么是懒加载:

所谓懒加载(lazy)就是延时加载,延迟加载。

什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载。

至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,

因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,

我们让数据在需要的时候才进行加载,这时我们就用到了懒加载。

比如部门ENTITY和员工ENTITY,部门与员工1对多,如果lazy设置为 false,那么只要加载了一个部门的po,就会根据一对多配置的关系把所有员工的po也加载出来。但是实际上有时候只是需要用到部门的信息,不需要用到 员工的信息,这时员工po的加载就等于浪费资源。如果lazy设置为true,那么只有当你访问部门po的员工信息时候才回去加载员工的po的信息。

hibernate3.0中lazy有三个值,true,false,proxy,默认的是lazy="proxy".
具体设置成什么要看你的需求,并不是说哪个设置就是最好的。
假如在student对象中包含一个head对象
如果你确定在用student对象的时候就要用到head对象里的属性,那你就设置立即加载,因为设置立即加载那么在查询student的同时就会查询 student的head,hibernate就会在查询的时候关联两张表从而生成的sql就可能只有一条。而如果你设置的是延迟加载,那么肯定会要生成 1+N条sql语句:其中“1”是查询student的语句,“N”是根据N个student的id去查询head的N条语句。而且,延迟加载是要用到的 时候才去执行查询,这样系统判断那里需要加载,那里不需要加载也需要时间,性能上肯定就不如立即加载了!
如果,你是有的地方需要用到student的时候才用到head属性,那么你就设置成延迟加载,因为查询2张表的数据肯定要比查询1张表的数据消耗大。
到低要怎样设置就要看你的实际需求了

延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。
A、实体对象的延迟加载 
如果想对实体对象使用延迟加载,必须要在实体的映射配置文件中进行相应的配置,如下所示:
<hibernate-mapping>
<class name=”com.neusoft.entity.User” table=”user” lazy=”true”>
   ……
</class>
</hibernate-mapping>
    通过将class的lazy属性设置为true,来开启实体的延迟加载特性。如果我们运行下面的代码:
User user=(User)session.load(User.class,”1”);(1)
System.out.println(user.getName());(2)
当运行到(1)处时,Hibernate并没有发起对数据的查询,如果此时通过一些调试工具,观察此时user对象的内存快照,会惊奇的发现,此时返 回的可能是User$EnhancerByCGLIB$$bede8986类型的对象,而且其属性为null,这是怎么回 事?session.load()方法会返回实体对象的代理类对象,这里所返回的对象类型就是User对象的代理类对象。在Hibernate中通过使用 CGLIB,来实现动态构造一个目标对象的代理类对象,并且在代理类对象中包含目标对象的所有属性和方法,而且所有属性均被赋值为null。通过调试器显 示的内存快照,可以看出此时真正的User对象,是包含在代理对象的CGLIB$CALBACK_0.target属性中,当代码运行到(2)处时,此时 调用user.getName()方法,这时通过CGLIB赋予的回调机制,实际上调用CGLIB$CALBACK_0.getName()方法,当调用 该方法时,Hibernate会首先检查CGLIB$CALBACK_0.target属性是否为null,如果不为空,则调用目标对象的getName 方法,如果为空,则会发起数据库查询,生成类似这样的SQL语句:select * from user where id=’1’;来查询数据,并构造目标对象,并且将它赋值到CGLIB$CALBACK_0.target属性中。
这样,通过一个中间代理对象,Hibernate实现了实体的延迟加载,只有当用户真正发起获得实体对象属性的动作时,才真正会发起数据库查询操作。 所以实体的延迟加载是用通过中间代理类完成的,所以只有session.load()方法才会利用实体延迟加载,因为只有session.load()方 法才会返回实体类的代理类对象。

两种处理方法:
一、这是延时加载的问题,把有关联的所有pojo类,在hibernate.cfg.xml文件中。一般在many-to-one中,set标签内中设lazy="false" 。
二、用OpenSessionInViewFilter过滤器,注意hibernateFilter过滤器和struts2过滤器在映射时的先后顺序。同时要配置事物处理,否则会导致session处于只读状态而不能做修改、删除的动作。

在web.xml文件中加入一个filter如下:注意要加载 struts过滤器前面。

<!-- 配置opensessionInViewFilter 用于解决懒加载问题-->  <filter>     <filter-name>OpenSessionInViewFilter</filter-name>     <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>  </filter><filter-mapping>    <filter-name>OpenSessionInViewFilter</filter-name>    <url-pattern>*.action</url-pattern></filter-mapping>
分析原理:
作用是拦截".action“结尾的请求,OpensessionInViewFilter的作用是将session延时关闭。
Hibernate中懒加载错误的解决方法 - 九鬼 - 朝阳起又落
 
在action类中会调用service层的方法,然后调用session.一般当session访问结束之后就会将session关闭.
比如我们要得到user的列表,默认情况下是不会将其中的roles的所有信息全部查出来的,最多只给出一个id,或者是name.
但是我们这个时候如果要打印roles的类容,就会报懒加载异常了.因为hibernate根本就没有将role的数据查出来.



来自csdn的懒加载解释

最近在做一个项目,采用的是SSH2框架,要实现一个级联功能,具体实现如下:

Department部门包括许多下级Department,在查询第一级Department时,一切OK,但是当查询第一级Department下的子Department时,出现了如下错误,其中Department.hbm.xml中的配置文件为

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <many-to-one name="parent" class="Department"column="parentId"></many-to-one>  

        这里表示Department实体中有parent这个属性,一个parent有多个Department,也就是多对一关系

以及Stacktraces

1、org.apache.jasper.JasperException:javax.el.ELException: Error reading 'name' on typecn.itcast.oa.domain.Department_$$_javassist_1

。。。。。省略。。。。。。

2、javax.el.ELException: Error reading 'name' ontype cn.itcast.oa.domain.Department_$$_

javassist_1

。。。。。省略。。。。。。

3、org.hibernate.LazyInitializationException:could not initialize proxy - no Session

。。。。。省略。。。。。。

 

在上面的三条错误消息中,第三条为关键错误提示LazyInitializationException(懒加载异常在默认情况下,hibernate为懒加载),这意味着在读取数据的时候,Session已经关闭。

 

解决办法:

1、  设置懒加载为false,在默认情况下,hibernate为懒加载,因此需要设置不为懒加载,在Department.hbm.xml中设置如下:

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <many-to-one name="parent"class="Department" column="parentId"lazy="false"></many-to-one>  

         lazy值设置为false,也就是说,当加载了父Department后,他的所有子Department都会被加载,这就会出现另外一个问题:当父Department下有很多子Department时,会加载所有的子Department,会造成性能很低。

那么我们能不能把他改为用的时候才加载,不用的时候则不加载?(默认还是懒加载,但是要你在用的时候能找到Session,能找到Session就能从数据库中读取数据)

2、采用拦截器


上面代表一次请求,需要经过Action和拦截器,左边方框为Action,中间方框为Result(页面),右边方框为Filter拦截器。

         Action表示我们要执行的结果,在Action里调用的是Service业务方法(Service方框),我们常用的做法是在Service中开和关事物,以及openSession和close Session,由于我们是在Result(页面)中才使用到懒加载的属性(此时Session已经关闭)。为了解决这个问题,必须要把close Session这一步推迟到Result后才能关闭。这里我们采用的是spring中OpenSessionInViewFilter(这是一个过滤器)来实现。

具体代码如下:

Department.hbm.xml中的配置保持不变,如下所示:

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <many-to-one name="parent" class="Department"column="parentId"></many-to-one>  
在web.xml中添加一个过滤器,如下所示:
[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <!-- 配置Spring的OpenSessionInViewFilter过滤器,以解决Hibernate的懒加载异常(LazyInitializationException) -->  
  2.     <filter>  
  3.        <filter-name>OpenSessionInViewFilter</filter-name>  
  4.     <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>  
  5.     </filter>  
  6.     <filter-mapping>  
  7.        <filter-name>OpenSessionInViewFilter</filter-name>  
  8.        <url-pattern>*.action</url-pattern>  
  9.     </filter-mapping>  

这样就可以实现需要的时候才加载,不需要的时候不加载,同时默认页为懒加载

 

一般情况下,如果没有使用Filter或者将lazy设置为false的话,在Action方法中使用完Session就会关闭Session,因此如果在Action方法外使用Session中的数据就会报懒加载异常

         OpenSessionInViewFilter的作用是将Session的关闭放到Filter中,因此,我们在Action方法外使用Session时,就不会出现Session已经关闭的异常了。

 

下面这张图跟上面这张图差不多,可以参考一下。

 

什么是懒加载?

    有一个Student类,它有一个属性Teacher t对象,当你load一个Student s 时,这个时候t = null,当你调用s.getT时,它才去加载t对象,也就是说:当你需要使用t的时候,才去加载至于proxy

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <class name="com.test.Student"table="student" lazy="true">  
  2. 这种方式等价于  
  3. <class name="com.test.Student"table="student" proxy="com.test.Student">  



0 0
原创粉丝点击