使用JdbcTemplate执行DELETE语句遇列索引越界问题求助
问题场景
执行以下JdbcTemplate代码时触发报错:
jdbcTemplate.update("DELETE FROM my_table WHERE created <= (NOW() - interval '? milliseconds')", 1);
报错信息:
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [DELETE FROM my_table WHERE created <= (NOW() - interval '? milliseconds')]; The column index is out of range: 1, number of columns: 0.; nested exception is org.postgresql.util.PSQLException: The column index is out of range: 1, number of columns: 0.
在SQL编辑器中直接执行硬编码参数的语句可正常运行:
select * from my_table where created <= (NOW() - interval '1 milliseconds');
原因分析
你的推测完全正确:占位符?被包裹在单引号的字符串字面量'? milliseconds'中,JDBC驱动会将其视为普通文本,不会识别为可绑定的参数位置,导致代码传递的参数1找不到对应的占位符,触发索引越界错误。
解决方案
提供三种可行的写法,均能让JdbcTemplate正确识别参数占位符:
方案1:使用PostgreSQL的make_interval函数(推荐)
利用PostgreSQL内置的make_interval函数直接指定毫秒参数,无需拼接字符串:
jdbcTemplate.update("DELETE FROM my_table WHERE created <= NOW() - make_interval(milliseconds => ?)", 1);
方案2:拼接字符串后转换为interval类型
将数字参数与单位字符串拼接,再显式转为interval类型:
jdbcTemplate.update("DELETE FROM my_table WHERE created <= NOW() - (? || ' milliseconds')::interval", 1);
方案3:基于基础interval做乘法
用固定的1毫秒interval乘以参数值,实现动态时长:
jdbcTemplate.update("DELETE FROM my_table WHERE created <= NOW() - INTERVAL '1 millisecond' * ?", 1);
以上三种写法都能保持预编译语句的安全性,避免SQL注入风险,同时解决占位符无法识别的问题。
内容的提问来源于stack exchange,提问作者rmf

