hibernate的懒加载问题

hibernate的懒加载问题

问题的发现

在使用ssh框架写一个小型论坛的时候,想要实现首页的分页功能,但是有一个问题,在取得发帖这个实体的时候,需要不只是显示实体的title,还想获得实体的另一个实体user,从而获得发帖人的信息,具体为昵称,以下为jsp前台代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<c:forEach items="${StoryList }" var="story">
<ul class="mdui-list">
<a href="${pageContext.request.contextPath }/story_show.action?story_id=${story.story_id}">
<li class="mdui-list-item mdui-ripple">
<div class="mdui-list-item-avatar"><img src="avatar1.jpg"/></div>
<div class="mdui-list-item-content">
<div class="mdui-list-item-title mdui-list-item-two-line">${story.story_title}</div>
<div class="mdui-list-item-text mdui-list-item-one-line"> ${story.user.username}发表
</div>
</div>
</li>
</a>
</ul>
</c:forEach>

但是问题出现了,不能正确加载story实体的user(已经在story实体里定义了User)报错如下

1
2
javax.el.ELException: Error reading 'username' on type cn.haigeek.entity.User_$$_jvste64_1
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

解决

通过查询了解到出错的原因是因为hibernate的懒加载问题

什么是懒加载

在Hibernate中,查询方法有两个,分别是get()和load(),这两种方法的不同就是load()拥有懒加载的特性。Load()方法就是在查询某一条数据的时候并不会直接将这条数据以指定对象的形式来返回,而是在你真正需要使用该对象里面的一些属性的时候才会去数据库访问并得到数据。他的好处就是可以减少程序本身因为与数据库频繁的交互造成的处理速度缓慢。

通用解决

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

    1
    <many-to-one name="user" class="cn.haigeek.entity.User" column="usid" lazy="false"></many-to-one>

    把lazy的值设置为false,但是这种方式会默认加载user对象的全部属性,十分消耗资源,不推荐使用

  2. 在ssh框架中可使用spring提供的openSessionView,由spring进行管理,在web.xml配置文件添加

    1
    2
    3
    4
    5
    6
    7
    8
    <filter>  
    <filter-name>OpenSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>OpenSessionInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    但是我已经配置此种方式,结果还是不能正确加载,但这个方式是可用的,注意要放在web.xml的靠前位置,要在struts过滤器的上方

我遇到的问题的解决方案

已经配置了OpenSessionInViewFilter,但是还不能正确加载,后来仔细分析发现是以下原因

  1. 由于我需要分页,所以在hibernate中使用的自定义hql语句,即

    1
    2
    3
    4
    String hql="from Story order by story_id desc ";
    Query query = session.createQuery(hql);//执行查询操作
    query.setFirstResult((pageNow - 1) * pageSize);
    query.setMaxResults(pageSize);

    使用这种方式需要先获取session

  2. 我的dao实现层是调用了HibernateDaoSupport方式,我出错的原因就是错误使用了获取session的方法,我获取session的方法为:

    1
    Session session=getSessionFactory().openSession();

    而正确获取session的方法为(较为推荐的一种):

    1
    Session session=this.getHibernateTemplate().getSessionFactory().getCurrentSession();

    这种方式是从spring管理的sessionFactory中创建一个绑定线程的session。Spring会根据该线程的执行情况来自动判断是关闭session还是延迟关闭。这样做可以避免手动的管理实务,同时一个线程最多开启和关闭一次session又可以提高程序的性能。到这里就可以理解为什么我已经使用了懒加载,但是依旧不能正确加载,既然hibernate的session交给了spring管理,就需要使用spring管理的SessionFactory进行操作,不需要手动加上session.close()

总结

  1. 使用HibernateDaoSupport要注意各种方法的实现
  2. hibernate的懒加载模式需要多加留意