报错提示:org.hibernate.LazyInitializationException: could not initialize proxy [xx.entity.xx] - no Session
项目框架背景:springCloud +Jpa
问题出现背景:系统使用Spring Cloud Stream 实现了 Rabbit MQ 的binder ,通过 @StreamListener 监听mq的消息,在监听的具体方法中调用了service层的方法 ,这个错误是这个方法中报的。

问题分析:从字面上看是通过jpa 去获取懒加载属性的数据时,Session已经关闭。

在具体分析问题之前,先科普一下:

 FetchType.LAZY和FetchType.EAGER什么区别?FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。一般情况下,属性用到的几率大,要马上到数据库查,用急加载;有些属性使用到的可能性比较小,随着其他属性查询出来比较耗费资源,所以想让它用了才查数据库,用懒加载就好了。

具体原因:由于表之间存在ManyToMany ManyToOne OneToMany时,由于lazy属性默认设置为true,总结起来就是session的关闭是在真正使用数据(这时候发送sql语句)之前。 即用对象的属性的时候,session已经关闭,导致该异常的发生!那就有个疑问了,session为什么关闭了?
默认情况下,开启一个事务,就会打开session,结束一个事务后,session就会关闭,意思就是说默认情况下session是在事务中打开的。一般的项目事务都是配置在service方法,就是运行一个service方法的时候,就会首先开启事务,然后运行该方法,然后关闭事务。是因为在事务之外,或者说你调用的地方在不在service方法中,很有可能在MVC层中,比如JSP,和 action或controller中调用,就会找不session。如果想在是事务之外,也就是在service方法以外让session有效,可以使用filter,专门用于在事务外提供session。

解决方案:本次项目中实体类中添加了 @ManyToOne(fetch = FetchType.LAZY)
(1)将上面的注解改成 @ManyToOne(fetch = FetchType.EAGER);
(2)如果实在是考虑到性能的话,必须使用懒加载,就将获取懒加载的属性放入事务中;
此次异常我的处理方式是,由于异步调用逻辑无法控制获取主类属性和懒加载属性在一个事务中,又想使用懒加载的方式,所以采用懒加载属性在使用之前,作为参数传入的方式解决,也就是说通过别的事务去获取值传入,这样其他逻辑使用到这个类的时候还是采用懒加载的方式,只是这次调用特殊处理了。

此问题还会深入研究,待更新!!!!

最后强烈推荐这篇文章:JPA Session 一劳永逸