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

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

解决org.hibernate.LazyInitializationException的正确姿势的更多相关文章

  1. 【bug记录】jpa 解决org.hibernate.lazyinitializationexception could not initialize proxy - no session

    前言 最近开发项目比较忙,Spring Cloud的笔记得稍稍放放了,下午出来个bug,恶心的不行,功能很简单,也没有什么级联或复杂的映射关系,就是一直在报三个异常 Caused by: com.fa ...

  2. 解决org.hibernate.LazyInitializationException: could not initialize proxy - no Session懒载入问题

    问题描写叙述: Struts Problem Report Struts has detected an unhandled exception: Messages: could not initia ...

  3. jpa 解决org.hibernate.lazyinitializationexception could not initialize proxy - no session

    org.hibernate.LazyInitializationException: could not initialize proxy [org.alan.entity.SysUser#1] - ...

  4. 剖析和解决Python中网络粘包的正确姿势

    目录 1.粘包及其成因 1.1.粘包产生 1.2.粘包产生的原因 2.尝试解决粘包 2.1.指定数据包的长度 2.2.固定数据包的长度 2.3.用函数实现多次调用发送数据 3.解决粘包问题的正确姿势 ...

  5. dubbo序列化hibernate.LazyInitializationException could not initialize proxy - no Session懒加载异常的解决

    dubbo序列化,hibernate.LazyInitializationException could not initialize proxy - no Session懒加载异常的解决 转载声明: ...

  6. 总结懒加载的解决方法(全)org.hibernate.LazyInitializationException: could not initialize proxy - no Session

    如下错误:org.hibernate.LazyInitializationException: could not initialize proxy - no Session 原因是懒加载的问题,因为 ...

  7. Ubuntu 解决wifi无法打开的问题 安装NVIDIA显卡驱动的正确姿势

    游戏本型号Y7000 win10 Ubuntu16.04双系统 解决wifi无法打开的问题 解决方法: 1.打开终端输入:rfkill list all 出现如下提示::       可以看到,优先级 ...

  8. org.hibernate.LazyInitializationException异常解决办法

    org.hibernate.LazyInitializationException异常failed to lazily initialize a collection...的解决方案使用hiberna ...

  9. 在Linux(ubuntu server)上面安装NodeJS的正确姿势

    上一篇文章,我介绍了 在Windows中安装NodeJS的正确姿势,这一篇,我们继续来看一下在Linux上面安装和配置NodeJS. 为了保持一致,这里也列举三个方法 第一个方法:通过官网下载安装 h ...

随机推荐

  1. DDD实战课(实战篇)--学习笔记

    目录 DDD实践:如何用DDD重构中台业务模型? 领域建模:如何用事件风暴构建领域模型? 代码模型(上):如何使用DDD设计微服务代码模型? 代码模型(下):如何保证领域模型与代码模型的一致性? 边界 ...

  2. Java课程设计-算术运算测试(D级) 齐鲁工业大学 计科20-1 王瀚垠 202003010033

    Java课程设计-算术运算测试(D级) 齐鲁工业大学 计科20-1 王瀚垠 202003010033 目录 1.项目简介 2.项目采用技术 3.功能需求分析 4.项目亮点 5.项目功能架构图和UML类 ...

  3. 为什么代码规范要求SQL语句不要过多的join?

    面试官:有操作过Linux吗? 我:有的呀 面试官:我想查看内存的使用情况该用什么命令 我:free 或者 top 面试官:那你说一下用free命令都可以看到啥信息 我:那,如下图所示 可以看到内存以 ...

  4. Mysql慢SQL分析及优化

    为何对慢SQL进行治理 从数据库角度看:每个SQL执行都需要消耗一定I/O资源,SQL执行的快慢,决定资源被占用时间的长短.假设总资源是100,有一条慢SQL占用了30的资源共计1分钟.那么在这1分钟 ...

  5. Oracle11g RAC详解

    升级变化:          Oracle10g,高版本都是由低版本升级得到的.要装10.2.0.4,必须先安装database10.2.0.1,然后给这个ORACLE_HOME打补丁p6180189 ...

  6. 七、JavaSE语言基础之方法

    关于方法的几个简单概念 关于方法的学习,先来明确几个简单的概念: 方法的作用:处理数据(把原始数据通过指定的算法处理后得到结果数据) 方法:在类中定义的具有特定功能的代码块 方法的意义(作用):提高代 ...

  7. vue 快速入门 系列 —— vue loader 下

    其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...

  8. Web自动化之iframe切换

    一.如何判断元素是否在iframe中 选中要操作的元素,通过下方的父节点查看是否存在iframe,存在则元素在iframe,需要切换至iframe中进行元素的操作 二.iframe切换方式一 1.方式 ...

  9. 跟我一起学Go系列:Go gRPC 安全认证方式-Token和自定义认证

    Go gRPC 系列: 跟我一起学Go系列:gRPC安全认证机制-SSL/TLS认证 跟我一起学 Go 系列:gRPC 拦截器使用 跟我一起学 Go 系列:gRPC 入门必备 接上一篇继续讲 gRPC ...

  10. Docker安装MySQL8.0

    环境 CentOS 7.5 Docker 1.13.1 MySQL 8.0.16 安装 拉取镜像 默认拉取最新版本的镜像 $ docker pull mysql 如果要指定版本,使用下面的命令 $ d ...