Spring JPA中where参数为null查询失效问题求助
问题:Spring JPA @Query中参数为null时查询失效
问题复现
复现代码可本地构建,核心代码如下
环境信息
使用Spring Boot Web、JPA、MySQL开发RESTful接口:
- Spring Boot父版本:2.7.0
- Java版本:Oracle 11
代码实现
仓库接口
public interface TestRepository extends JpaRepository<Test, Long> { @Query("SELECT t FROM Test t WHERE (?1 IS NULL OR t.name LIKE %?1%)") Page<Test> findAll(String keyword, Pageable pageable); }
接口控制器
@GetMapping("/ab") public Page<Test> ab(CustomPageable pageable) { // 测试数据: // INSERT INTO test VALUES(1, 'aa', 1); // INSERT INTO test VALUES(2, 'a', 2); // INSERT INTO test VALUES(3, 'b', 3); return testRepository.findAll(pageable.getKeyword(), pageable); }
CustomPageable模型
public static class CustomPageable extends PageRequest { @Getter private final String keyword; protected CustomPageable(int page, int size, Sort sort) { super(page, size, sort); keyword = null; } public CustomPageable(int page, int size, Sort sort, String keyword) { super(page, size, sort); this.keyword = keyword; } }
问题现象
当pageable.getKeyword()为null时(请求地址:http://localhost:8181/ab?size=3&sort=name,DESC&page=0),期望返回包含3条数据的分页结果,但实际返回空页。
日志信息
2022-10-02 15:40:00.967 INFO 6560 --- [nio-8181-exec-6] p6spy : #1664700000967 | took 2ms | statement | connection 4| url jdbc:mysql://localhost:3306/test select test0_.id as id1_0_, test0_.name as name2_0_, test0_.status as status3_0_ from test test0_ where ? is null or test0_.name like ? order by test0_.name desc limit ? select test0_.id as id1_0_, test0_.name as name2_0_, test0_.status as status3_0_ from test test0_ where '%org.hibernate.jpa.TypedParameterValue@d41192d%' is null or test0_.name like '%org.hibernate.jpa.TypedParameterValue@d41192d%' order by test0_.name desc limit 3;
注意查询语句中出现的'%org.hibernate.jpa.TypedParameterValue@d41192d%',这是异常核心点。
补充说明
当传入keyword参数时(请求地址:http://localhost:8181/ab?size=3&sort=name,DESC&page=0&keyword=a),查询结果正常。
问题请求
请解释为何keyword为null时HQL查询失效?是代码问题还是JPA的bug?如何修复该问题?
注意:必须使用@Query注解的HQL实现,请勿推荐CriteriaBuilder等其他方式。
原因分析
这是代码写法导致的参数解析异常,并非JPA bug。具体来说:
- HQL中
%?1%的写法存在歧义,Hibernate会将%和?1拼接成字符串字面量,而非将?1作为独立参数绑定 - 当keyword为null时,Hibernate不会把
%?1%解析为%null%,而是直接把参数对象(TypedParameterValue)的toString结果拼入字符串,生成'%org.hibernate.jpa.TypedParameterValue@d41192d%' - 此时查询条件
'%xxx%' IS NULL永远为false,同时name LIKE '%xxx%'也匹配不到任何数据,最终返回空页
修复方案
方案1:使用命名参数+HQL函数拼接通配符
修改HQL,利用Hibernate的CONCAT函数在查询语句中拼接通配符,确保参数被正确绑定:
public interface TestRepository extends JpaRepository<Test, Long> { @Query("SELECT t FROM Test t WHERE (:keyword IS NULL OR t.name LIKE CONCAT('%', :keyword, '%'))") Page<Test> findAll(@Param("keyword") String keyword, Pageable pageable); }
这种写法无需在业务代码中拼接字符串,直接传入原始keyword即可:
- 当keyword为null时,
CONCAT('%', null, '%')返回null,条件(:keyword IS NULL OR ...)生效,返回所有数据 - 当keyword有值时,
CONCAT生成正确的模糊匹配字符串,查询正常
方案2:命名参数+业务层拼接通配符
如果偏好在业务层处理字符串拼接,可拆分参数:
public interface TestRepository extends JpaRepository<Test, Long> { @Query("SELECT t FROM Test t WHERE (:keyword IS NULL OR t.name LIKE :keywordPattern)") Page<Test> findAll(@Param("keyword") String keyword, @Param("keywordPattern") String keywordPattern, Pageable pageable); }
控制器中处理参数:
@GetMapping("/ab") public Page<Test> ab(CustomPageable pageable) { String keyword = pageable.getKeyword(); String keywordPattern = keyword != null ? "%" + keyword + "%" : null; return testRepository.findAll(keyword, keywordPattern, pageable); }
内容的提问来源于stack exchange,提问作者SoT
相关产品推荐
相关产品推荐

