Spring Boot JPA降序取首条记录报FETCH语法错误如何解决
问题根因
FETCH FIRST 1 ROWS ONLY是Oracle原生SQL的分页语法,你在@Query中默认编写的是JPQL,Hibernate 5.4以下版本的JPQL解析器不识别该语法,因此在FETCH关键字处抛出语法异常,和你的初步判断一致。另外你现有JPQL存在字段引用不规范的问题:WHERE条件和ORDER BY子句中除第一个字段外,其余字段都未加uploadedFile.别名前缀,后续即使解决FETCH语法问题,也可能抛出属性未找到的错误。
可落地解决方案
按通用性优先级从高到低排列:
方案1:使用Spring Data JPA自带分页能力(最推荐,跨数据库兼容)
不需要在JPQL中写数据库专属的取数语法,直接给方法加Pageable参数指定取第一条即可,所有适配的数据库都能正常运行:
@Query("SELECT uploadedFile.id FROM UploadedFile uploadedFile " + "WHERE uploadedFile.p_file_type = 'branch' " + "and uploadedFile.p_file_status='Processed' " + "and uploadedFile.p_is_file_process_with_error = 0 " + "order by uploadedFile.c_created_date desc ") public String findLatestBranchCodeFile(Pageable pageable); // 业务层调用示例,指定取第0页、每页1条数据即可拿到目标结果 // String latestFileId = uploadedFileRepo.findLatestBranchCodeFile(PageRequest.of(0, 1));
方案2:标记为原生SQL查询,直接使用Oracle语法
如果不想修改方法签名,可以给@Query加nativeQuery = true参数,直接写Oracle原生SQL,注意表名、字段名要和数据库实际定义一致,不要用Java实体的属性名:
@Query(value = "SELECT id FROM uploaded_file " + "WHERE p_file_type = 'branch' " + "and p_file_status='Processed' " + "and p_is_file_process_with_error = 0 " + "order by c_created_date desc " + "FETCH FIRST 1 rows only ", nativeQuery = true) public String findLatestBranchCodeFile();
如果使用Oracle 11g及更早版本,原生SQL不支持FETCH FIRST语法,需要替换为ROWNUM写法,注意必须先排序再取ROWNUM,否则会拿到错误数据,示例:
SELECT id FROM ( SELECT id FROM uploaded_file WHERE p_file_type = 'branch' AND p_file_status='Processed' AND p_is_file_process_with_error = 0 ORDER BY c_created_date DESC ) WHERE ROWNUM = 1
方案3:升级Hibernate版本到5.4及以上
Hibernate从5.4版本开始在JPQL中支持FETCH FIRST n ROWS ONLY语法,如果你使用的Spring Boot版本在2.2及以上,默认绑定的Hibernate版本已经满足要求,只需要补全所有字段的实体别名前缀即可正常运行原有查询:
@Query("SELECT uploadedFile.id FROM UploadedFile uploadedFile " + "WHERE uploadedFile.p_file_type = 'branch' " + "and uploadedFile.p_file_status='Processed' " + "and uploadedFile.p_is_file_process_with_error = 0 " + "order by uploadedFile.c_created_date desc " + "FETCH FIRST 1 rows only ") public String findLatestBranchCodeFile();
避坑提示
- 编写JPQL时,所有引用实体属性的位置都必须带上声明的实体别名前缀,否则会出现属性映射失败的问题。
- 老版本Oracle使用ROWNUM取排序后第一条时,必须把排序逻辑放在内层子查询,不能直接在同层SQL加
WHERE ROWNUM=1,因为Oracle会先执行ROWNUM筛选再排序,返回的结果不符合预期。
内容的提问来源于stack exchange,提问作者Rahul SK
相关产品推荐
相关产品推荐

