JDBC执行带IN子句的update语句无报错却更新0行如何解决?
问题修复方案
根因说明
JDBC 预编译语句的 IN 子句不支持通过单个参数传入逗号分隔的多值,你传入的拼接ID字符串会被整体识别为单个字符串类型参数,与 bigint 类型的 foo_id 无法匹配,因此不会命中任何记录,更新行数为0。
推荐修复方案(使用 NamedParameterJdbcTemplate)
Spring 提供的 NamedParameterJdbcTemplate 原生支持 IN 子句集合参数传入,是该场景下最优解决方案:
- 首先替换 SQL 为命名参数写法
- 将拼接的 ID 字符串转为
Long类型集合传入 - 无需手动处理占位符拼接,框架自动适配
代码示例:
// 定义命名参数格式的更新SQL final String FOO_SQL_UPDATE_QUERY = "update foo_table set foo_ts = :fooTs where foo_id in (:fooIds);"; // 初始化NamedParameterJdbcTemplate,也可以直接注入Spring容器中的实例 NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); data.forEach((date, idStrBuilder) -> { // 将逗号分隔的ID字符串转为Long类型集合 List<Long> idList = Arrays.stream(idStrBuilder.toString().split(",")) .map(Long::parseLong) .toList(); // 组装参数 MapSqlParameterSource params = new MapSqlParameterSource() .addValue("fooTs", date, Types.TIMESTAMP) .addValue("fooIds", idList); // 执行更新 namedJdbcTemplate.update(FOO_SQL_UPDATE_QUERY, params); });
替代方案(原生JdbcTemplate实现)
如果不想使用命名参数模板,可以通过动态生成IN子句占位符的方式实现:
data.forEach((date, idStrBuilder) -> { List<Long> idList = Arrays.stream(idStrBuilder.toString().split(",")) .map(Long::parseLong) .toList(); // 动态生成和ID数量一致的占位符 String placeholders = String.join(",", Collections.nCopies(idList.size(), "?")); String updateSql = "update foo_table set foo_ts = ? where foo_id in (" + placeholders + ")"; // 组装参数数组,第一个元素是日期,后续是所有ID Object[] params = new Object[idList.size() + 1]; params[0] = date; for (int i = 0; i < idList.size(); i++) { params[i + 1] = idList.get(i); } // 执行更新 jdbcTemplate.update(updateSql, params); });
优化建议
- 建议将原
Map<Date, StringBuilder>改为Map<Date, List<Long>>存储ID集合,避免后续拆分字符串的性能损耗,也能防止拼接逗号时出现格式错误 - PostgreSQL 单条IN子句的参数数量建议控制在1000以内,如果ID数量过大可以分批执行更新,避免SQL过长影响性能
内容的提问来源于stack exchange,提问作者Shakti
相关产品推荐
相关产品推荐

