在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 框架中它们是怎么一起运作的
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.LAZY 更改为 FetchType.EARLY ,当我们需要用到关联类的大多数级联属性时,这算一种较为常规的修改方法
@OneToMany(mappedBy = "groupClass",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@OrderBy("name DESC")
private List<Student> students = new ArrayList<>();
Criteria criteria = session.createCriteria(GroupClass.class);
criteria.setFetchMode("students", FetchMode.EAGER);
对比以上方法,该方法是最高效的,查询时通过一条 SQL 语句实现从数据库中获取目标实体关联集合,解决N+1Select 查询效率问题