SpringBoot JPA执行select查询报错与日期比较问题排查
需要查询入职日期晚于指定日期、薪资高于指定阈值的员工数据,传入参数为startdate=2000-10-22、salary=10000时,执行查询抛出如下异常:
"org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for select queries [Select e FROM com.example.macademiaproject.model.Employee e where (:startDate is NULL or e.startDate < :startDate) AND (:salary is NULL or e.salary > :salary) ]; nested exception is java.lang.IllegalStateException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for select queries [Select e FROM com.example.macademiaproject.model.Employee e where (:startDate is NULL or e.startDate < :startDate) AND (:salary is NULL or e.salary > :salary) ]",
现有两点疑问:
- 是否可以直接使用
>、<这类比较符判断日期先后,例如判断2000/12/13 > 2001/5/8是否成立?如果不支持该如何正确实现日期比较? - 上述报错的产生原因是什么,该如何修复?
Repository层代码
@Transactional @Modifying @Query("Select e FROM Employee e " + "where " + " (:startDate is NULL or e.startDate < :startDate) " + " AND " + " (:salary is NULL or e.salary > :salary) ") void searchEmployees(@Param(value= "startDate")Date startDate,@Param(value = "salary") Integer salary);
Service层接口代码
void searchEmployees(Date startDate,Integer salary);
Service实现类代码
@Override public void searchEmployees(Date startDate,Integer salary){ employeeRepository.searchEmployees(startDate,salary); }
Controller层代码
@GetMapping(path="/search") public ResponseEntity<List<Employee>> searchEmployees(@RequestParam("startDate") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date startDate, @RequestParam("salary") Integer salary) { employeeService.searchEmployees(startDate,salary); return null; }
关于HQL日期比较的说明
只要实体类的日期字段、传入的查询参数都是标准日期类型(包括java.util.Date、java.sql.Date、JDK8+的LocalDate/LocalDateTime等),可以直接使用>、<、>=、<=比较符判断日期先后,Hibernate会自动完成类型适配,不需要额外写函数转换。
如果传入的是未做类型转换的日期字符串,不能直接用比较符判断:字符串会按照字典序逐字符比较,比如"2000/12/13"和"2000/5/8"比较时,逐字符匹配到月份位置,字符'1'的ASCII码小于'5',会判定前者更小,但实际12月晚于5月,结果完全不符合日期逻辑。这种情况需要先把字符串解析为日期类型,再做比较。
另外注意你的业务需求是查询入职日期晚于指定日期,对应的比较逻辑应该是e.startDate > :startDate,现有代码写的是<,逻辑本身是反的。
报错原因与修复方案
核心报错原因
Repository方法上错误使用了@Modifying注解:这个注解仅支持标记增删改类的DML操作(INSERT/UPDATE/DELETE),你的查询是SELECT类型的查询操作,Hibernate检测到修改操作的执行逻辑遇到SELECT语句,就会抛出Not supported for select queries异常。
除了核心报错外,现有代码还存在几个逻辑问题:
- Repository方法返回值定义为
void,无法返回查询到的员工列表结果 - 日期比较条件写反,和业务需求不匹配
- Service层、Controller层没有透传查询结果,Controller直接返回
null,前端拿不到数据 - Controller层的查询参数默认是必传,和你写的
参数为NULL则不触发条件过滤的逻辑不匹配,不传参数会直接报400错误
修复步骤
- 移除Repository方法上的
@Modifying注解,只读查询不需要该注解,如需加事务可配置@Transactional(readOnly = true)提升性能 - 将Repository、Service层的
searchEmployees方法返回值从void修改为List<Employee> - 修正HQL中的日期比较条件,将
e.startDate < :startDate改为e.startDate > :startDate,匹配"入职日期晚于指定日期"的需求 - 补全各层的结果透传逻辑,Controller将查询到的列表封装到响应中返回,同时给请求参数加上
required = false支持非必传
修复后的各层代码参考如下:
修复后Repository代码
@Query("Select e FROM Employee e " + "where " + " (:startDate is NULL or e.startDate > :startDate) " + " AND " + " (:salary is NULL or e.salary > :salary) ") List<Employee> searchEmployees(@Param(value= "startDate")Date startDate,@Param(value = "salary") Integer salary);
修复后Service接口代码
List<Employee> searchEmployees(Date startDate,Integer salary);
修复后Service实现类代码
@Override public List<Employee> searchEmployees(Date startDate,Integer salary){ return employeeRepository.searchEmployees(startDate,salary); }
修复后Controller代码
@GetMapping(path="/search") public ResponseEntity<List<Employee>> searchEmployees( @RequestParam(value = "startDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date startDate, @RequestParam(value = "salary", required = false) Integer salary) { List<Employee> employeeList = employeeService.searchEmployees(startDate,salary); return ResponseEntity.ok(employeeList); }
内容的提问来源于stack exchange,提问作者RohanRider

