Spring Boot 2.7.16中JPA方法名自动生成查询异常排查
Spring Boot 2.7.16 JPA Repository 查询抛出 EntityNotFoundException 问题排查与解决
核心原因
- 数据不一致:数据库中存在
AccessPlant记录关联的Plant实体已被删除(比如ID为12的Plant不存在,但AccessPlant中仍保留该ID的关联),JPA加载关联实体时触发异常。 - 关联加载策略变更:Spring Boot 2.x对应的Spring Data JPA/Hibernate版本升级后,关联实体的默认
FetchType或加载逻辑发生变化。若AccessPlant中关联Plant的FetchType为EAGER,查询User时会强制加载关联的Plant,一旦Plant不存在就抛出异常。 - 查询逻辑隐性变化:
- 自定义查询中
LEFT OUTER JOIN后追加ap.accessPlantKey.plant.plantId IN (...)条件,实际将左连接转为内连接效果,仅返回存在匹配AccessPlant的User;若这些AccessPlant关联的Plant不存在,加载时触发异常。 - 方法名自动生成的查询(如
findAllByAccessPlantsSet_AccessPlantKey_Plant_PlantIdIn)在新版本中生成的SQL逻辑改变,默认尝试加载嵌套关联的Plant实体。
- 自定义查询中
- 模糊匹配范围问题:精确匹配时返回的User对应的AccessPlant关联的Plant均存在,而模糊匹配返回的结果中包含关联了不存在Plant的User,触发加载异常。
解决办法
1. 清理脏数据(优先方案)
检查数据库中AccessPlant表的plant_id字段,删除或修复关联不存在Plant的记录。例如删除所有plant_id为12且对应的Plant不存在的AccessPlant记录,从根源解决数据不一致问题。
2. 调整关联加载策略
将AccessPlant中关联Plant的FetchType改为LAZY,避免查询User时自动加载Plant实体:
// AccessPlant类中关联Plant的字段 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "plant_id") private Plant plant;
仅当业务逻辑主动访问plant属性时才会加载,避免不必要的关联加载触发异常。
3. 修改查询语句,避免加载关联实体
如果查询仅需使用Plant的ID做条件,无需加载Plant实体,直接使用AccessPlantKey中的plantId字段(假设是嵌入式主键的一部分),而非通过关联实体访问:
@Query("SELECT DISTINCT u FROM User u LEFT OUTER JOIN u.accessPlantsSet ap WHERE ap.accessPlantKey.plantId IN (?1)") Page<User> findAllByPlantId(List<Long> plantIds, Pageable pageParam);
这样JPA不会尝试加载Plant实体,自然不会触发不存在的异常。
4. 使用Hibernate忽略不存在的关联(兼容方案)
若无法清理数据且需兼容脏数据,可使用Hibernate特有的@NotFound注解,忽略不存在的关联实体:
// AccessPlant类中关联Plant的字段 @ManyToOne @NotFound(action = NotFoundAction.IGNORE) private Plant plant;
此注解会让Hibernate将不存在的Plant设为null而非抛出异常,但会降低JPA的跨提供商兼容性,谨慎使用。
5. 修正左连接查询逻辑
若需保留左连接(允许返回无AccessPlant的User),调整WHERE条件处理ap为null的情况:
@Query("SELECT DISTINCT u FROM User u LEFT OUTER JOIN u.accessPlantsSet ap WHERE (?1 IS NULL OR ap.accessPlantKey.plant.plantId IN (?1))") Page<User> findAllByPlantId(List<Long> plantIds, Pageable pageParam);
结合FetchType.LAZY使用,避免加载不存在的Plant实体。
内容的提问来源于stack exchange,提问作者Sreejesh
相关产品推荐
相关产品推荐

