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

时间:2022-12-12 20:02:28

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

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

提一嘴 get 和 load 的区别:【详细可以查看这篇博客 load() 和 get() 的区别

1.get() 采用立即加载方式,而 load() 采用延迟加载;
2.get() 方法执行的时候,会立即向数据库发出查询语句,而 load() 方法返回的是一个代理(此代理中只有一个 OID 属性),只有等真正使用该对象属性的时候,才会发出 sql 语句并执行


3.如果数据库中没有对应的记录 , get() 方法返回的是 null . 而 load() 方法出现异常 ObjectNotFoundException

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

public UserModel getUserById(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";
}

报出的异常如下:

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

原因:

这是因为数据层提供的 load() 方法具有延迟加载的特性,在调用 load方法时,Hibernate 不会立即执行 sql 语句,而是动态的生成一个 UserModel 的代理对象实例,这个代理对象只具有 OID,也就是 uuid 值【这个值来自于形参,而不是数据库】,该对象的其他信息都没有。

这个动态代理对象所具有的特性有:

  1. 代理类对象和真实的 UserModel 对象无异,但是只具有 OID,其他属性均为 null,所以会比较省内存,这也是延迟加载的好处
  2. 当第一次访问这个代理类对象时,调用器 getXxx 方法时不会出问题,Hibernate 才会初始化这个代理对象,并会自动去执行 sql 语句去查询其数据库中的数据,【也就是 Service 层中的同一个方法,一旦这个方法弹栈,即事务提交结束,session 被关闭,】。

当需要这个只具有 OID 的代理对象传递到了业务层(如果在事务关闭session之前立马进行二次查询,就不会出问题),此时对象仍是一个延迟加载对象(只具有 OID),如果传递到了表现层(action内),此时该对象依然是延迟加载对象(但此时早在业务层,方法早已弹栈,事务就已经将 session 关闭了,一旦要进行二次查询就会报异常, 因为 session对象已经被刷新了,也就是连接断开了,session 中存储的数据早已丢失),值得一说的是在表现层使用二次查询是比较正常的需求。

解决这个问题有三个途径:

  • 在全局取消延迟加载数据
  • 在局部取消延迟加载数据
  • 将 session 的范围扩大,扩大到表现层【此时就是 action 内 】

第一种:在全局取消延迟加载数据,【这里的全局指的是某个表,并不是整个数据库】

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.msym.entity">
<!-- 取消对于 user_db 表的延迟加载属性 -->
<class name="User" table="user_db" lazy="false" >
<id name="id">
<generator class="identity"/>
</id>
<property name="password" />
<property name="name" />
<property name="nickName" />
<property name="age" />
<property name="birthday" />
</class>
</hibernate-mapping>

优点:简单方便;

缺点:对于这个表,取消了延迟加载,在很多情况下会导致内存占用大的问题,取出了原本不需要的数据。

第二种:在局部取消延迟加载,【在 Service 层和 dao 层都可以局部取消延迟加载属性】

public UserModel getUserById(Long uuid) {
UserModel u = this.getHibernateTemplate().load(UserModel.class, uuid);
Hibernate.initialize(u);
return u; //return this.getHibernateTemplate().get(UserModel.class, uuid);
}

优点:相比第一种,更加具有针对性,更加的灵活;

缺点:代码量稍稍多了一点点【但是也不要紧】。

第三种:扩大 session 的范围,

也就是要做到当前 session 在 Service 层的方法弹栈后不随事务的提交而关闭,直到一次请求与响应完成才关闭。这时用到的一个技术就是 openSessionInView,将 session 与当前请求对应的线程绑定在一起【此 session 并非浏览器会话级别的 session,而是数据库连接的 session】,这需要在 web.xml 中配置一个过滤器,这时注解的事务的任务就是开启事务,刷新事务;关闭事物的功能交给了 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> <init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param> <init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param> <init-param>
<param-name>flushMode</param-name>
<param-value>AUTO</param-value>
</init-param>
    </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>

OpenSessionInViewFilter 配置了三个参数:singleSession,sessionFactoryBeanName ,flushMode,

这几个属性都有其默认值,分别是,true,SessionFactory,MANUAL【手动提交事务】。

其中只是将 flushMode的默认属性修改了,是为了防止出现 FlushModel 相关的异常。

因为上面使用的是【org.springframework.orm.hibernate3.support.OpenSessionInViewFilter】这些属性在其中都能找到,

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

但是如果使用的是 hibernate4.support 的话,这些属性的位置就发生了变化,
其中:

(1) . sessionFactoryBeanName 还是在 OpenSessionInViewFilter里面,默认值也是 sessionFactory 如下图:

Hibernate 中 load() 方法导致的 noSession 异常
       (2) . singleSession 默认值也是 true

(3) . flushMode 属性来自于 Session 这个接口,默认值也是MANUAL【手动提交事务】。

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

它表示事务的提交方式,有如下几种:

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

优点:配置简单,

缺点:拦截了所有请求,效率不高,如果对象导航层级较多,会导致页面的加载速度变慢,延长了 Session的生命周期,加重了服务器的负担。

总结:

        到最后推荐使用第二种,也就是局部取消延迟加载特性,灵活性更好。

Hibernate 中 load() 方法导致的 noSession 异常的更多相关文章

  1. hibernate中load&comma;get&semi;find&comma;iterator&semi;merge&comma;saveOrUpdate&comma;lock的区别

    hibernate中load,get;find,iterator;merge,saveOrUpdate,lock的区别 转自http://www.blogjava.net/bnlovebn/archi ...

  2. Hibernate 中 load&lpar;&rpar; 和 get&lpar;&rpar; 的区别

    get 和 load 方式都是是根据 id 取得一个记录.下边详细说一下 get 和 load 的不同,因为有些时候为了对比也会把 find 加进来. 1.从返回结果上对比: load 方式检索不到的 ...

  3. J2EE进阶&lpar;十六&rpar;Hibernate 中getHibernateTemplate&lpar;&rpar;方法使用

    J2EE进阶(十六)Hibernate 中getHibernateTemplate()方法使用   spring 中获得由spring所配置的hibernate的操作对象,然后利用此对象进行,保存,修 ...

  4. Hibernate中load与get&comma;update与merge方法的区别

    1.load()与get()的区别: (1)load()读取 User user = (User)session.load(User.class, userId); (2)get()读取 User u ...

  5. Hibernate中load与get的区别

    1.get()采用立即加载方式,而load()采用延迟加载; ①get()方法执行的时候,会立即向数据库发出查询语句;(查询顺序:内部缓存,数据库) ②load()方法返回的是一个代理(此代理中只有一 ...

  6. Hibernate中Query&period;list&lpar;&rpar;方法报IllegalArgumentException异常

    最近在使用Hibernate开发项目,在写好hql语句,并初始化Query对象,执行Query.list()方法时,应用报IllegalArgumentException异常.经网上查询,现已经基本决 ...

  7. jq中 load&lpar;&rpar;方法 简介

    load()方法会在元素的onload事件中绑定一个处理函数.如果处理函数绑定给window对象,则会在所有内容(包括窗口,框架,对象和图像等)加载完毕后触发,如果处理函数绑定在元素上,则会在元素的内 ...

  8. hibernate中load和get方法的区别

    1.读取时机不同(当lazy=true的时候)    load是采用延迟机制(load语句不读库,等使用非主键时才去读库),而get不采用延  迟机制(get语句时马上读库): 2.搜索不到数据时的情 ...

  9. hibernate中一种导致a different object with the same identifier value was already associated with the session错误方式及解决方法

    先将自己出现错误的全部代码都贴出来: hibernate.cfg.xml <?xml version="1.0" encoding="UTF-8"?&gt ...

随机推荐

  1. go并发3

    Go语言并发的设计模式和应用场景 以下设计模式和应用场景来自Google IO上的关于Goroutine的PPT:https://talks.golang.org/2012/concurrency.s ...

  2. java之接口interface

    接口 1.多个无关的类可以实现同一个接口 2.一个类可以实现多个无关的接口 3.与继承关系类似,接口与实现类之间存在多态性 4.定义java类的语法格式 < modifier> class ...

  3. ASP&period;NET服务器端执行耗时操作的工作记录

    公司之前有这样一个业务需求: 一名同事做出文件a0和b0,然后将a0加密为a1.b0加密为b1:再将文件a0.a1.b0和b1上传至服务器M:同时要将服务器N上的数据表添加一条记录,该记录的ID就是前 ...

  4. 导出到Excel并且取消默认的科学计算法

    导出Excel的代码很多,其中这种最简单: protected void btnDCAll_Click(object sender, EventArgs e)        {            ...

  5. Android点击Button实现功能的几种方法

          Android中Button控件应该算作是比较简单的控件,然而,它的使用频率却是非常的高,今天,我在这里总结了三种常用的点击Button实现其功能的方法.       1.很多时候,我们在 ...

  6. ASP&period;NET Session丢失问题原因及解决方案

    正常操作情况下会有ASP.NET Session丢失的情况出现.因为程序是在不停的被操作,排除Session超时的可能.另外,Session超时时间被设定成60分钟,不会这么快就超时的. ASP.NE ...

  7. Linux的一些简单命令&lpar;二&rpar;

    1.查看防火墙状态:service iptables status 2.开启防火墙:service iptables start 3.关闭防火墙:service iptables stop 4.创建目 ...

  8. InputStream字节输入流

    1.字节输入流——硬盘中数据写出到内存*解析使用: 根据文件存储原理,8位二进制组成为一个字节,换算后的数值在0-127则查询ASCII码表,其他则查询系统默认表,如简体中文查询GBK表: 2.Fi ...

  9. CentOS 6&period;5静态IP的设置(NAT和桥接联网方式都适用)

    不多说,直接上干货! 为了方便,用Xshell来.并将IP设置为静态的.因为,在CentOS里,若不对其IP进行静态设置的话,则每次开机,其IP都是动态变化的,这样会给后续工作带来麻烦.为此,我们需将 ...

  10. VirtualBox虚拟机网络环境解析和搭建-NAT、桥接、Host-Only、Internal、端口映射

    一.NAT模式 特点: 1.如果主机可以上网,虚拟机可以上网 2.虚拟机之间不能ping通 3.虚拟机可以ping通主机(此时ping虚拟机的网关,即是ping主机) 4.主机不能ping通虚拟机 应 ...