Spring Boot 3.x迁移后JPA Repository查询验证失败求助
迁移Spring Boot 3.x后JPA查询创建失败问题解决
问题现象
将项目迁移至Spring Boot 3.1.4(Spring Boot 3.2搭配Hibernate 6.4时问题复现)后,IndemniteRepository中的findAllForExport方法无法创建查询,报错提示查询验证失败。
核心错误日志
Could not create query for public abstract java.util.List com.xx.xx.xx.repository.IndemniteRepository.findAllForExport(com.xx.xx.xx.domain.penalite.AvisPenaliteStatutEnum,java.math.BigDecimal,java.util.List); Reason: Validation failed for query for method public abstract java.util.List com.xx.xx.xx.repository.IndemniteRepository.findAllForExport(com.xx.xx.xx.domain.penalite.AvisPenaliteStatutEnum,java.math.BigDecimal,java.util.List)
Hibernate解析错误
Caused by: org.hibernate.query.sqm.ParsingException line 1:241 no viable alternative at input 'SELECTindFROMIndemniteindWHEREind.indemniteAFacturer>:indemniteAFacturerANDind.avis.statut=:statutAND(:statut!=com.xx.xx.xx.domain.penalite.AvisPenaliteStatutEnum.POUR_FACTUREANDind.avis.facturationEnAttenteISTRUE'
相关Repository代码
@Repository public interface IndemniteRepository extends JpaRepository<Indemnite, Long>, JpaSpecificationExecutor<Indemnite> { @Query("SELECT ind FROM Indemnite ind " + "WHERE ind.indemniteAFacturer > :indemniteAFacturer " + "AND ind.avis.statut = :statut " + "AND (:statut != com.xx.xx.xx.domain.penalite.AvisPenaliteStatutEnum.POUR_FACTURE AND ind.avis.facturationEnAttente IS TRUE OR ind.avis.facturationEnAttente IS FALSE) " + "AND ind.avis.typePenalite.name IN :typePenalite " + "ORDER BY ind.avis.date ASC") List<Indemnite> findAllForExport(@Param("statut") AvisPenaliteStatutEnum statut, @Param("indemniteAFacturer") BigDecimal indemniteAFacturer, @Param("typePenalite") List<TypePenaliteEnum> typePenalite); }
问题原因
Hibernate 6.x对JPQL的解析规则比旧版本更严格:
- 直接在JPQL中引用Java枚举全限定类名的写法不再被支持
- 原查询中的逻辑表达式未明确优先级,进一步触发解析失败
解决方案
1. 修正枚举引用方式
在Spring Data JPA的@Query中,需通过SpEL表达式引用Java枚举常量,格式为:#{T(全限定类名).枚举值},替换原直接引用枚举类的写法。
2. 明确逻辑表达式优先级
调整括号包裹范围,避免AND/OR优先级导致的逻辑歧义。
修改后的查询代码
@Repository public interface IndemniteRepository extends JpaRepository<Indemnite, Long>, JpaSpecificationExecutor<Indemnite> { @Query("SELECT ind FROM Indemnite ind " + "WHERE ind.indemniteAFacturer > :indemniteAFacturer " + "AND ind.avis.statut = :statut " + "AND ((:statut != #{T(com.xx.xx.xx.domain.penalite.AvisPenaliteStatutEnum).POUR_FACTURE} AND ind.avis.facturationEnAttente IS TRUE) OR ind.avis.facturationEnAttente IS FALSE) " + "AND ind.avis.typePenalite.name IN :typePenalite " + "ORDER BY ind.avis.date ASC") List<Indemnite> findAllForExport(@Param("statut") AvisPenaliteStatutEnum statut, @Param("indemniteAFacturer") BigDecimal indemniteAFacturer, @Param("typePenalite") List<TypePenaliteEnum> typePenalite); }
替代方案:使用枚举字符串值
若不想使用SpEL,可直接使用枚举的字符串值进行比较(需保证枚举name()值与数据库存储值一致):
@Query("SELECT ind FROM Indemnite ind " + // ... 其他条件 "AND ((:statut != 'POUR_FACTURE' AND ind.avis.facturationEnAttente IS TRUE) OR ind.avis.facturationEnAttente IS FALSE) " + // ... 其他条件
内容的提问来源于stack exchange,提问作者Amir Choubani
相关产品推荐
相关产品推荐

