You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot接口未传code参数触发SQL语法异常排查

SQL语法异常(WHERE)附近错误)排查与解决

问题重现

测试API端点时,不传code参数会触发SQL语法异常,错误提示WHERE)附近存在语法问题;传入正确/错误code时返回正常结果,直接在MySQL CLI执行带null参数的同类查询也能正常运行。

错误日志

2022-08-24 11:51:54.283  WARN 9836 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1064, SQLState: 42000
2022-08-24 11:51:54.283 ERROR 9836 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE) FROM rates WHERE (date BETWEEN '2022-01-01' AND '2022-03-01') AND ((null ' at line 1
2022-08-24 11:51:54.292 ERROR 9836 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/api/v1] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause

java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE) FROM rates WHERE (date BETWEEN '2022-01-01' AND '2022-03-01') AND ((null ' at line 1

相关代码

Repo层代码

@Query("SELECT * FROM rates WHERE (date BETWEEN :startDate AND :endDate) AND ((:code IS NULL OR rate_code = :code) AND rate > 0)", nativeQuery = true)
fun getByDateRange(startDate: Date, endDate: Date, code: String?, pageable: Pageable): Page<RateEntity>

Controller层代码

@GetMapping("/historic")
fun ratesForDateRange(
    @RequestParam startDate: Optional<Date>,
    @RequestParam endDate: Optional<Date>,
    @RequestParam(required = false) code: String?,
    @RequestParam(required = false) pageNumber: Int?
): Page<RateEntity> {
    val lastDate = Date.valueOf(START_DATE)
    val page = pageNumber ?: 0
    println(code)
    return repo.getByDateRange(
        startDate.orElse(lastDate),
        endDate.orElse(UtilFunctions.getCurrentSQLDate()),
        code,
        PageRequest.of(page, 50)
    )
}

问题原因

当使用原生查询配合Pageable分页参数时,Spring Data JPA会自动生成对应的COUNT查询来统计总记录数。当code参数为null时,自动生成的COUNT查询在解析动态条件时出现逻辑错误,生成了WHERE)这种无效语法,最终导致SQL执行失败。

直接在MySQL CLI执行正常是因为手动编写的查询不涉及Spring Data自动生成COUNT查询的逻辑,而API调用时分页必须依赖COUNT查询,因此触发了问题。

解决方案

方法1:手动指定COUNT查询

在@Query注解中添加countQuery属性,手动编写正确的统计查询,完全避免Spring Data自动生成错误的COUNT语句:

@Query(
    value = "SELECT * FROM rates WHERE (date BETWEEN :startDate AND :endDate) AND ((:code IS NULL OR rate_code = :code) AND rate > 0)",
    countQuery = "SELECT COUNT(*) FROM rates WHERE (date BETWEEN :startDate AND :endDate) AND ((:code IS NULL OR rate_code = :code) AND rate > 0)",
    nativeQuery = true
)
fun getByDateRange(startDate: Date, endDate: Date, code: String?, pageable: Pageable): Page<RateEntity>

方法2:改用JPQL查询(实体映射正确时优先使用)

如果RateEntity已正确映射rates表,改用JPQL可以让Spring Data更好地处理动态参数和分页逻辑,从根源避免原生查询的COUNT生成问题:

@Query("SELECT r FROM RateEntity r WHERE r.date BETWEEN :startDate AND :endDate AND (:code IS NULL OR r.rateCode = :code) AND r.rate > 0")
fun getByDateRange(startDate: Date, endDate: Date, code: String?, pageable: Pageable): Page<RateEntity>

注意:JPQL中使用实体属性名(如rateCode)而非数据库列名(rate_code),需确保实体字段与数据库列的映射关系正确。

方法3:调整原生查询条件结构

修改原生查询的条件顺序,简化逻辑,帮助Spring Data的COUNT查询生成器正确解析:

@Query("SELECT * FROM rates WHERE date BETWEEN :startDate AND :endDate AND rate > 0 AND (:code IS NULL OR rate_code = :code)", nativeQuery = true)
fun getByDateRange(startDate: Date, endDate: Date, code: String?, pageable: Pageable): Page<RateEntity>

这种方法可靠性不如前两种,但在部分场景下可以快速解决问题。

内容的提问来源于stack exchange,提问作者Nick Wilde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:12:20