首页 > 代码库 > Hibernate 中 load() 方法导致的 noSession 异常

Hibernate 中 load() 方法导致的 noSession 异常

之所以要写这个,是因为最近碰到了一个延迟加载的 load() 导致出现 noSession 的异常。

解决这个问题需要用到一个本地线程的对象,也就是 ThreadLocal 类,之前写过关于这个对象,可以看这个博客本地线程 ThreadLocal 类

 

      我在数据层中封装了一个 load() 方法,根据用户 Id 获取用户对象:

public UserModel get(Long uuid) {
        return this.getHibernateTemplate().load(UserModel.class, uuid);
        //return this.getHibernateTemplate().get(UserModel.class, uuid);
    }

 

        当在业务层调用了这个方法后,获得一个 userModel 对象,当展示用户信息时,就会报出 noSession 问题,

public String get(){
        //报出了 org.hibernate.LazyInitializationException: could not initialize proxy - no Session异常
        UserModel temp = userEbi.get(1L);
        System.out.println(temp.getName());
        System.out.println(temp.getAge());
        return "haha";
    }

报出的异常如下:

技术分享

 

      这是因为数据层提供的 load() 方法具有延迟加载的特性,也就是获得对象只有 OID ,其他的信息都没有,只有当需要的时候才会去查询(这叫二次查询,延迟加载其他数据),如果二次查询之前,这个只具有 OID 的对象传递到了业务层(如果在事务关闭session之前立马进行二次查询,就不会出问题),此时对象仍是一个延迟加载对象(只具有 OID),如果传递到了表现层(action内),此时该对象依然是延迟加载对象(但此时早在业务层,事务就已经将session关闭了,一旦要进行二次查询就会报异常,因为session对象已经被刷新了,也就是连接断开了,session中存储的数据早已丢失)。

  但是在表现层使用二次查询是比较正常的,也就是要做到当前session不关闭,直到一次请求完成才关闭。这时用到的一个技术就是 openSessionInView

这需要在 web.xml 中配置一个过滤器,用于将每个请求对象 request 和 session 对象绑定在一起(这里的session是与数据库交互的session,并不是浏览器的session会话),至于session的关闭,在一次请求结束时 session 会被自动关闭,这时注解的事务的任务就是开启事务,刷新事务;关闭事物的功能交给了 OpenSessionInViewFilter 这个类了,因为这个过滤器配置在一个,请求对象 request 最先接触到它,请求结束时的最后一个过滤器也是它(这就是过滤器链了,往返要都过滤一次)。代码在下面:(一定要将 OpenSessionInViewFilter 配置在所有过滤器的前面)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!-- applicationContext对象加载仅加载一次,服务器启动时加载,使用web中的监听器机制 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    
    <!-- OpenSessionInView解决noSession问题,一定要配置在核心过滤器的前面 -->
    <filter>
        <filter-name>openSessionInView</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>openSessionInView</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- struts核心过滤器 -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

Hibernate 中 load() 方法导致的 noSession 异常