在JPA 中使用关系注解例如@ManyToOne
、 @OneToMany
,其中fetchType
参数默认为 LAZY
对象懒加载 ;当关联实体类在数据库没有记录存在时,会报出LazyInitializationException
错误:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.cn.entity.test.GroupClass.students, could not initialize proxy - no Session
错误出现原因 ,简单来说就是,以延迟方式加载对象时,此时 Hibernate 与数据库通信时 session
已经管理了,没有相关 session
可以通信了。
深入理解错误之前,需要了解 session
, Lazy initialisation
以及 Proxy Object
等基本概念,以及在 Hibernate 框架中它们是怎么一起运作的
- 1,session 代表一个持久化 上下文,对应用和数据库之间代表一种通信交流,类似于客户端与服务器之间的通信 session (会话)
- 2,懒加载意味对象不会直接从 session context 加载,触发条件是从代码层获取的时候才会加载。
- 3,Hibernate 创建一个 动态
Proxy Object
类作为代理来访问数据库,并且仅当我们首次使用对象时,它才访问数据库;
LazyInitializationException
错误发生是当我们尝试借助代理对象从数据库中获取一个懒加载对象,但 Hibernate session
此时已经关闭,从而报错
该错误解决方法:
方法一,修改配置
在配置文件 yml
或 properties
中将 hibernate.enable_lazy_load_no_trans
参数设为 true,该参数 用于声懒加载对象的的获取全局方式,默认为 false
,打开它意味着当每次以懒加载方式获取关联实体对象时,都将运行在新的事务中的新会话中。
spring:
jpa:
# 控制台打印 sql
show-sql: false
hibernate:
ddl-auto: none
properties:
# 解决 LazyInitializationException 错误
hibernate.enable_lazy_load_no_trans: true
方法存在一定的弊端:会导致 N+1
问题,Hibernate 首先执行一个 Select
来获取 目标实体类,然后根据实体类关联的属性,再执行 N
(N
代表关联属性的个数 ) 个 Select
操作查询实体类的相关属性;
方法二,修改关联注解中 FetchType
将 FetchType.LAZY
更改为 FetchType.EARLY
,当我们需要用到关联类的大多数级联属性时,这算一种较为常规的修改方法
@OneToMany(mappedBy = "groupClass",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@OrderBy("name DESC")
private List<Student> students = new ArrayList<>();
方法三,利用 Hibernate Criteria API 方法
Criteria criteria = session.createCriteria(GroupClass.class);
criteria.setFetchMode("students", FetchMode.EAGER);
对比以上方法,该方法是最高效的,查询时通过一条 SQL 语句实现从数据库中获取目标实体关联集合,解决N+1
Select 查询效率问题