Spring Data JPA 中LEFT JOIN FETCH不生效,关联实体不存在未返回null问题
问题根源
你遇到的两类异常本质上是两个叠加问题导致的:
- 无物理外键约束下Hibernate的默认关联策略:因为两张表没有配置物理外键,学生记录删除后选修课表的
STUDENT_ID仍会保留。Hibernate默认遇到「关联主键值存在但对应实体不存在」的场景时,会直接抛出EntityNotFoundException,而不是返回null。 - 分页场景下
LEFT JOIN FETCH失效:当JPA查询同时使用LEFT JOIN FETCH和Page类型分页返回时,Hibernate会优先执行count查询计算总页数,这一步会忽略fetch指令,先查询主表ID做分页,再单独加载主表实体,关联的student属性仍保持懒加载状态,所以你写的LEFT JOIN FETCH实际没有生效。- 业务方法不加事务时:Repository方法执行完Session就关闭,此时访问懒加载的
student代理要初始化就会抛出LazyInitializationException - 业务方法加事务时:Session仍存活,初始化代理时去查询学生表发现无对应记录,就会抛出
EntityNotFoundException
- 业务方法不加事务时:Repository方法执行完Session就关闭,此时访问懒加载的
解决方案(满足懒加载要求,无需修改为EAGER策略)
第一步:配置关联不存在时自动返回null
修改OptionalCourse类的student属性,新增Hibernate提供的@NotFound注解,指定关联实体不存在时直接忽略、返回null:
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "STUDENT_ID" , referencedColumnName="STUDENT_ID", insertable = false, updatable = false ) // 新增以下注解 @NotFound(action = NotFoundAction.IGNORE) private Student student;
第二步:修正分页场景下关联加载逻辑
你已经定义了OptionalCourse.student命名实体图,直接在Repository方法上加上@EntityGraph注解即可,无需修改原有的查询逻辑,就能保证分页查询时同时加载student关联,避免懒加载异常:
@Repository @Transactional(rollbackFor = Exception.class) public interface OptionalCourseRepository extends JpaRepository<OptionalCourse,Integer> { // 新增EntityGraph注解,指定加载student关联 @EntityGraph(value = "OptionalCourse.student", type = EntityGraph.EntityGraphType.FETCH) @Query(value = "SELECT oc FROM OptionalCourse oc where oc.studentId > :studentId", countQuery = "SELECT count(oc.id) FROM OptionalCourse oc where oc.studentId > :studentId") public Page<OptionalCourse> findOptionalSubjectPagesByStudentId(@Param("studentId") Integer studentId, Pageable pageable); }
效果验证
修改完成后,查询时会自动左连接关联学生表:
- 学生记录存在:
oc.getStudent()返回正常的Student实体 - 学生记录不存在:
oc.getStudent()直接返回null,不会抛出任何异常 - 即使业务方法不加事务,因为关联已经在查询时完成初始化,也不会出现懒加载异常
内容的提问来源于stack exchange,提问作者Varun
相关产品推荐
相关产品推荐

