阿了嗝欢的小白日记——No session异常的发生原因和解决方案

今天被朋友问到No Session异常,虽然很困想睡会午觉,不过为了能保持写博客的好习惯,这会儿就拿休息时间来写一篇关于No session的文章。

No Session异常是一种经典的异常!为什么这么说呢,我个人认为因为这个小小的异常虽然很好解决,但光解决不是目的,了解其产生原因和解决问题的原理才是根本。原因涉及的知识点很基础,却容易被我们忽视,需得我们注意!

废话不多说!


产生原因:

在SSH框架中无论是直接使用hibernate框架还是使用Spring data 整合Jpa做持久层框架进行持久化操作,为了节省资源效率,在对多表关联的对象进行查询的时候,都有一个默认设定:一对多关系中,查询一方对象时,默认对其的多方(也就是属性中的Set集合)进行延迟加载,查询多方对象时是非延迟加载。多对多中两方都是延迟加载。

延迟加载延迟加载是hibernate中用于提高查询效率的一种措施,具体实现方式是在调用查询方法时,通过CGLib动态代理方式在内存中创建一个代理对象,当你去访问这个对象的除了ID外的属性时,才会发出Sql语句。hibernate的对延迟加载的具体实现不是今天的主题,不多做赘述。可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。

延迟加载对于效率的提高也显而易见:查询一方时如果当前请求并不需要使用多方的数据,此时,如果多方的数据量很大,那么这个查询不但是无用操作,还数倍的提高了资源的浪费。

所以当我们在框架默认的设置下在Web层对一个查询到的对象(此对象并非只是多方的情况下)进行操作或和页面渲染时使用到对象时,此时,我们已经出了service业务层,也就是说session(或者EntityManager)已经被close掉了。所以会出现 No session异常。至于为什么使用Jpa不会报No EntityManager异常就不知道了,可能hibernate的开发人员想生命session才是hibernate的核心吧得意

 

原因已经找到了 那么问题就好解决了:

方案一

我们可以选择关闭延迟加载,因为hibernate的session多表查询中一方查询时是默认加载,现在我们可以设置延迟加载属性为lazy=false。没有了延迟加载,自然也就没有了No Session的问题。

优点:简单,暴力。得意

缺点:这种解决办法有点太死板的,因为把延迟加载这么好的特性给活生生的去掉了,总感觉很亏。

个人认为这种办法有两个使用场景:1.当前一方模块的请求基本都会用的到多方数据。

2.确定当前一方模块所对应的多方数据量不大。

  以上两种场景虽然可以选择放弃延迟加载,且能保证不会浪费过多的资源,但肉少就不食肉了么?明显这不是最好的解决方案。

方案二:

2我们还可以把session的生命周期放置到表现层,这样的话,就可以在应用延迟加载特性下,也可以获取想要的数据。这时通常采取的操作是使用OpenSessionView,在表现层的web.xml中进行配置即可。配置过滤器,页面渲染完毕,再关闭session

代码如下:

<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> 

 

这个配置可以将session交给当前线程管理,演唱session的生命周期。实现在请求进入web层之前就创建session并开启线程,在响应结束,页面渲染完成之后关闭session。

这样便可以在保留延迟加载的情况下避免No session。


优点:保留了延迟加载,依然简单,暴力。得意

缺点:由于这个操作也是针对全局的。也就是说所有请求进入服务器都会拦截,开启session绑定线程,在后台处理完之后关闭session。所有!重点是所有!所以这种方法的性能也不是很好。


差点忘了,当使用的是Spring data 整合Jpa(用hibernate时间Jpa)的方式时配置OpenSessionView虽然没了No session的问题 ,但是会出一个新的sessionFactory的异常,因为底层是用的是Jpa进行持久化操作,全程并没有session的参与,此时我们需要配置OpenEntityManagerInView。如下:

<filter>  

<filter-name>openEntityManagerInView</filter-name>  

<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>  

</filter>  

<filter-mapping>  

<filter-name>openEntityManagerInView</filter-name>  

<url-pattern>/*</url-pattern>  

</filter-mapping> 


方案三:(次方案只适用于含有Json的操作,也是我本次碰到的No Session的解决方案)

当在我们的操作过程中使用到Json时(如各种异步请求),并且当前请求不需要用的集合属性时,可以选择进行如下操作。

案例距离代码如下(fixedAreas是多对一关系中一方的多方Set属性的变量名):

优点:完美的提高程序性能 保留了延迟加载并且防止了配置OpenSessionView对全局性能的影响。

缺点:应用场景有限。


方案四,五.....:

其实还有很多种方式。比如使用动态增强的方式对需要的请求进行加强,实现在web层查询或使当前请求变为立即加载。方案五,六,方案七等等等。

这个方案网上见过人说过,没试过,理论上应该是可以的。至于其他方案....我觉得代码是死的人是活的,而且不同场景选择不同,想要实现一个需求方式还是很多很多的,就看各自的业务需求和技术能力了得意



本来已经一中午写完了,强行划掉了我整整两个大中午才完成,速度有待提高。


—————困的一B的阿欢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值