首页 > 其他分享 >解决org.hibernate.LazyInitializationException的正确姿势

解决org.hibernate.LazyInitializationException的正确姿势

时间:2023-04-04 17:55:54浏览次数:45  
标签:lazy hibernate java spring LazyInitializationException com org open

转载:https://www.cnblogs.com/surging-dandelion/p/15085605.html

 

项目运行过程中,一个报错信息,报错信息如下:

org.hibernate.LazyInitializationException: could not initialize proxy [xxx.domain.Guild#CF12263C600F4BCABC9293D3FABE4B42] - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:169) ~[hibernate-core-5.3.9.Final.jar!/:5.3.9.Final]
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:309) ~[hibernate-core-5.3.9.Final.jar!/:5.3.9.Final]
    at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45) ~[hibernate-core-5.3.9.Final.jar!/:5.3.9.Final]
    at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.3.9.Final.jar!/:5.3.9.Final]
    at xxx.domain.Guild$HibernateProxy$58NSae2j.getName(Unknown Source) ~[classes!/:0.0.9-SNAPSHOT]
    at xxx.task.TaskJiaoFuService.guildName(TaskJiaoFuService.java:181) ~[classes!/:0.0.9-SNAPSHOT]
    at xxx.task.TaskJiaoFuService.result2JiaoFuDetail(TaskJiaoFuService.java:122) ~[classes!/:0.0.9-SNAPSHOT]
    at xxx.task.TaskJiaoFuService.parseResult(TaskJiaoFuService.java:106) ~[classes!/:0.0.9-SNAPSHOT]
    at xxx.task.TaskJiaoFuService.queryV4(TaskJiaoFuService.java:91) ~[classes!/:0.0.9-SNAPSHOT]
    at xxx.AbstractExportStrategy.query(AbstractExportStrategy.java:65) ~[classes!/:0.0.9-SNAPSHOT]
    at xxx.ExportService.exportAndPersistence(ExportService.java:130) [classes!/:0.0.9-SNAPSHOT]
    at xxx.ExportService.lambda$execute$0(ExportService.java:75) [classes!/:0.0.9-SNAPSHOT]
    at xxx.common.initialization.ContextCopyingTaskDecorator.lambda$decorate$0(ContextCopyingTaskDecorator.java:20) ~[classes!/:0.0.9-SNAPSHOT]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_181]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_181]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_181]

 

业务很简单,一个jpa的单表查询,获取属性的时候报错了

分析

JPA默认使用的懒加载,即使访问的是单个实体类,返回的对象也是代理,在获取对象属性的时候才会进行数据库查询,此时如果连接数据session已释放则会抛出上述异常
org.hibernate.LazyInitializationException在经常使用hibernate或者jpa的同学中可能经常遇到,网络上一搜,解决问题的方式有很多种,这里罗列一下:

  • 在spring boot的配置文件application.properties添加spring.jpa.open-in-view=true
  • 用spring 的OpenSessionInViewFilter
  • 在spring boot的配置文件application.properties添加spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
  • 在出问题的实体类上加@Proxy(lazy = false)
  • ……

spring.jpa.open-in-view

我们看下baeldung上是怎么说的,传送门:https://www.baeldung.com/spring-open-session-in-view

Session per request is a transactional pattern to tie the persistence session and request life-cycles together. Not surprisingly, Spring comes with its own implementation of this pattern, named OpenSessionInViewInterceptor, to facilitate working with lazy associations and therefore, improving developer productivity.

………

By default, OSIV is active in Spring Boot applications. Despite that, as of Spring Boot 2.0, it warns us of the fact that it's enabled at application startup if we haven't configured it explicitly:

 
spring.jpa.open-in-view is enabled by default. Therefore, database 
queries may be performed during view rendering.Explicitly configure 
spring.jpa.open-in-view to disable this warning

意思大致是,每个请求会话对于Spring来说都是一种事务模式,所以了我们默认给你开启了,用于提高开发效率,不过你如果没有显式配置的话,我还会给你一个warning告警。关于这个默认配置在github上争论也有不少:https://github.com/spring-projects/spring-boot/issues/7107
OSIV时序图如下:

项目中spring.jpa.open-in-view是设置为false的,代码获取实体类或者关联实体都是service中完成的,一般我们开启事务,在事务作用的上下文环境中去获取懒加载的数据是不会有任何问题的,且开启之后数据的session会等到整个request完成之后才会释放,其实是十分消耗性能的,之前有其他同学没有关闭open-in-view遇到的问题:https://www.cnblogs.com/thisismarc/p/13594399.html

结论:spring.jpa.open-in-view为true可以解决报错,不过不推荐,OpenSessionInViewFilter配置方案也PASS

spring.jpa.properties.hibernate.enable_lazy_load_no_trans

我们也看下baeldung上是怎么说的,传送门:https://www.baeldung.com/hibernate-lazy-loading-workaround
While using lazy loading in Hibernate, we might face exceptions, saying there is no session.
……
The recommended approach is to design our application to ensure that data retrieval happens in a single transaction. But, this can sometimes be difficult when using a lazy entity in another part of the code that is unable to determine what has or hasn't been loaded.

Hibernate has a workaround, an enable_lazy_load_no_trans property. Turning this on means that each fetch of a lazy entity will open a temporary session and run inside a separate transaction.

意思大致是,这是一种变通的做法,可以为每个懒加载的实体打开一个临时的会话,不过这个方法也是反人类的,因为如果延迟加载的关联实体越多,请求附加的连接也就越多,这会给数据库连接带来压力。在新事务中加载的每个关联,在每次关联初始化后都会强制刷新事务日志,所以大大的不建议使用。

@Proxy(lazy = false)

@Proxy(lazy = false)的意思和FetchType.EAGER类似,返回的是初始化好的实体,即关闭了懒加载,这个肯定不是我们想要的

推荐解决方式

回到我们的问题,单表懒加载报错,项目使用的是Springboot,事务都是显式的注解配置,查询的接口我们一般没有配置@Transactional注解,所以解决方法是在Service的查询方法上增加 @Transactional(readOnly = true) 注解来划分事务边界,这是比较推荐的做法,也复合编码规范,项目中如果有事务切面配置,把相关方法加到事务控制的范围中则也不会出现这个问题,如果还有其他更好的方式,欢迎留言

参考链接

https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/
https://www.baeldung.com/spring-open-session-in-view
https://github.com/spring-projects/spring-boot/issues/7107
https://www.cnblogs.com/thisismarc/p/13594399.html
https://www.baeldung.com/hibernate-lazy-loading-workaround

标签:lazy,hibernate,java,spring,LazyInitializationException,com,org,open
From: https://www.cnblogs.com/hanjun0612/p/17287238.html

相关文章

  • myBatis报错org.apache.ibatis.ognl.NoSuchPropertyException
    跑批任务时mybatis报错org.apache.ibatis.ognl.NoSuchPropertyException,重跑未出现报错,百度发现是由于mybatis依赖的Ognl版本OgnlRuntime.getMethodValue在并发情况下会存在并发问题,错误地返回null引起报错 以下是搜索该问题时找到的资料:https://github.com/1993hzh/tho......
  • nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http
    一、问题背景利用springboot上传大文件二、报错截图如下2023-04-0216:04:18,681ERROR[http-nio-63050-exec-6][GlobalExceptionHandler.java:58]-系统异常:Maximumuploadsizeexceeded;nestedexceptionisjava.lang.IllegalStateException:org.apache.tomcat.util.h......
  • junit单元测试报错:java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
    今天在复习的时候对对一些知识点进行巩固,用到了junit-4.12.jar,手动导入jar包,然后运行然后报错:java.lang.NoClassDefFoundError:org/hamcrest/SelfDescribing。刚开始我以为代码错了,看了看发现不是代码的问题,是导包的问题。然后查询了百度,发现了是版本的问题:然后说换个低版本的就......
  • base64 之坑----Base64后出现换行符 org.apache.commons.net.util.Base64()
    坑记录问题:base64后的结果会出现\r\n换行符, 复现:publicstaticvoidmain(String[]args){Stringkey="ssjsi21djsiej284858jweejrh34981dwde32323243232423423412121";Strings=java.util.Base64.getEncoder().encodeToString(key.getBytes(St......
  • Project 'org.springframework.boot:spring-boot-starter-parent:XXX' not found
    问题:Project'org.springframework.boot:spring-boot-starter-parent:XXX'notfound当spring-boot-starter-parent下面的版本报红时并不是这个版本不存在,而是因为idea会默认缓存Maven本地仓库已存在的中的依赖项。只是我们引入的的父依赖版本在本地仓库中不存在,所以就报......
  • org.apache.http.conn.scheme.SchemeRegistry
    出现此问题的机型:小米8,android系统版本为:9。应用为免费小说下载地址为 安卓免费小说点我下载解决方案见官方文档:https://developer.android.google.cn/about/versions/......
  • Spring4+Hibernate4事务小记
    学习Spring+Hibernate,非常强大的框架,为了追新,就直接从最高版本开始学习了,这要冒很大的风险,因为网上可查到的资料大多是针对旧版本的,比如Spring3,Hibernate3。根据我的测试,记......
  • Spring4 MVC Hibernate4集成
    一、   本文所用环境Spring4.0.3.RELEASEHibernate4.3.5.FinalMysql二、   工程目录 三、   Maven添加依赖用Maven创建项目,pom.xml如下:ViewCode四、  ......
  • Hibernate缓存机制
    2.0.14有注解功能没有2.1.6强大,如果前者需要使用注解需要引用后者的CORE核心包,项目中不需要用注解,所以我们选择2.0.14,根据自己的需要来选择   这是面试中经常问到的......
  • Hibernate 子类中不能用session 操作数据库
    Objectobj=this.getHibernateTemplate().execute(newHibernateCallback(){publicObjectdoInHibernate(Sessionsession)throwsHibernateExcep......