Spock测试时SQL语句出现H2语法错误,运行应用则正常
问题描述
仅在执行测试用例时出现SQL语法错误,运行应用或直接执行查询均正常。
报错信息
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar
Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement
涉及的SQL查询语句
SELECT C.idCampaign, SUM(DATEDIFF(IFNULL(CPM.actual_end, CURDATE()),CPM.actual_start)) AS daysPaused FROM Campaign C JOIN CampaignPauseManagement CPM ON C.idCampaign = CPM.idCampaign WHERE C.idCampaign IN (:campaignIds) GROUP BY C.idCampaign;
已尝试的无效操作
- 修改变量名
- 用
COALESCE替换IFNULL - 改用
CASE语句处理空值
以上操作均未解决问题,错误提示在SUM括号内的表达式末尾标记了[*],错误位置如下:
SELECT C.idCampaign, SUM(DATEDIFF(IFNULL(CPM.actual_end, CURDATE()),CPM.actual_start)[*]) AS daysPaused
解决方案
问题根源是测试环境使用的H2数据库和生产/运行环境的数据库(如MySQL、PostgreSQL)函数语法不兼容:
核心问题:
DATEDIFF参数差异
不同数据库中DATEDIFF的参数规则不一样:- 比如MySQL中是
DATEDIFF(结束时间, 起始时间),直接返回天数差; - 但H2中必须指定时间单位,且参数顺序为
DATEDIFF(单位, 起始时间, 结束时间),你的写法不符合H2的语法要求,才会报语法错误。
- 比如MySQL中是
修正后的SQL(适配H2)
SELECT C.idCampaign, SUM(DATEDIFF('DAY', CPM.actual_start, IFNULL(CPM.actual_end, CURRENT_DATE))) AS daysPaused FROM Campaign C JOIN CampaignPauseManagement CPM ON C.idCampaign = CPM.idCampaign WHERE C.idCampaign IN (:campaignIds) GROUP BY C.idCampaign;
这里把CURDATE()换成CURRENT_DATE,也是为了提升跨数据库的兼容性。
- 更优的测试环境适配方案
不用修改SQL,直接给H2开启目标数据库的兼容模式即可。比如生产用MySQL,就在H2的连接URL中添加;MODE=MySQL参数,让H2模拟MySQL的语法规则,这样测试用例就能直接使用生产环境的SQL,避免语法差异问题。
内容的提问来源于stack exchange,提问作者user3413540
相关产品推荐
相关产品推荐

