构建仅包含非空值的SQL UPDATE查询的最佳实践咨询
构建仅包含非空值的SQL UPDATE查询最佳实践
你现在用多个if判断加StringBuilder拼接的方式其实能解决问题,但确实有点繁琐,这里有几个更优雅的最佳实践可以参考:
一、借助ORM框架的动态SQL(推荐)
如果项目里用了MyBatis这类ORM框架,直接用它的动态SQL标签就能轻松实现,完全不用手动拼接SQL,还能从根源避免SQL注入问题。比如在Mapper XML里这么写:
<update id="updatePerson"> UPDATE PERSON <set> <if test="firstName != null and firstName != ''">FIRST_NAME = #{firstName},</if> <if test="middleName != null and middleName != ''">MIDDLE_NAME = #{middleName},</if> <if test="lastName != null and lastName != ''">LAST_NAME = #{lastName},</if> <if test="address != null and address != ''">ADDRESS = #{address},</if> </set> WHERE ID = #{id} </update>
二、原生Java代码优化(无框架场景)
如果必须用原生Java手动拼接,别再一个个判断逗号了,用列表收集非空的更新子句,最后再统一拼接,代码会清爽很多:
public void updatePerson(String id, String firstName, String middleName, String lastName, String address) { List<String> updateClauses = new ArrayList<>(); MapSqlParameterSource params = new MapSqlParameterSource("id", id); if (firstName != null && !firstName.isBlank()) { updateClauses.add("FIRST_NAME = :firstName"); params.addValue("firstName", firstName); } if (middleName != null && !middleName.isBlank()) { updateClauses.add("MIDDLE_NAME = :middleName"); params.addValue("middleName", middleName); } if (lastName != null && !lastName.isBlank()) { updateClauses.add("LAST_NAME = :lastName"); params.addValue("lastName", lastName); } if (address != null && !address.isBlank()) { updateClauses.add("ADDRESS = :address"); params.addValue("address", address); } // 没有要更新的字段时直接返回,避免执行无效SQL if (updateClauses.isEmpty()) { return; } String query = String.format("UPDATE PERSON SET %s WHERE ID = :id", String.join(", ", updateClauses)); // 用NamedParameterJdbcTemplate执行查询和参数绑定 namedParameterJdbcTemplate.update(query, params); }
这种方式的优势很明显:
- 不用手动处理逗号,
String.join(", ", updateClauses)会自动帮你用逗号分隔子句 - 逻辑清晰,每个参数的判断和子句添加一一对应
- 依然采用参数绑定,完全规避SQL注入风险
三、通用工具类封装(进阶)
如果多个业务场景都有类似的动态更新需求,可以封装一个通用工具类,减少重复代码:
public static <T> Pair<String, MapSqlParameterSource> buildUpdateClauses(String id, Map<String, T> fieldValueMap) { List<String> clauses = new ArrayList<>(); MapSqlParameterSource params = new MapSqlParameterSource("id", id); for (Map.Entry<String, T> entry : fieldValueMap.entrySet()) { T value = entry.getValue(); if (value != null && !(value instanceof String && ((String) value).isBlank())) { clauses.add(String.format("%s = :%s", entry.getKey(), entry.getKey())); params.addValue(entry.getKey(), value); } } return new Pair<>(String.join(", ", clauses), params); }
调用的时候就能大幅简化:
Map<String, String> fieldMap = new HashMap<>(); fieldMap.put("FIRST_NAME", firstName); fieldMap.put("MIDDLE_NAME", middleName); fieldMap.put("LAST_NAME", lastName); fieldMap.put("ADDRESS", address); Pair<String, MapSqlParameterSource> result = buildUpdateClauses(id, fieldMap); if (!result.getFirst().isEmpty()) { String query = String.format("UPDATE PERSON SET %s WHERE ID = :id", result.getFirst()); namedParameterJdbcTemplate.update(query, result.getSecond()); }
关键注意事项
- 绝对禁止直接拼接参数值:一定要用参数绑定(比如
:firstName这种占位符),防止SQL注入攻击 - 灵活处理空字符串:根据业务需求决定是否把空字符串/纯空格当作“无更新”的情况,上面的例子用
isBlank()做了过滤 - 避免无效更新:必须判断是否有更新子句,如果没有就直接返回,不要执行语法错误或无意义的SQL
你目前的实现思路大概是这样(截取你提供的代码片段):
public void updatePerson(String id, String firstName, String middleName, String lastName, String address) { StringBuilder query = new StringBuilder("UPDATE PERSON SET "); MapSqlParameterSource m...这种方式可行,但手动处理逗号容易出错,比如第一个参数为空时开头会多逗号,或者最后一个参数后残留逗号,用列表收集的方式就能彻底避免这类问题。
内容的提问来源于stack exchange,提问作者Sanjay Jain
相关产品推荐
相关产品推荐

