NamedParameterJdbcTemplate能防SQL注入,因其底层使用PreparedStatement,将:name参数转为?并用类型安全方法绑定,恶意字符无法进入SQL解析阶段;但仅适用于值位置,不支持表名、列名等SQL结构动态化。

NamedParameterJdbcTemplate 为什么能防 SQL 注入
因为它底层仍走 PreparedStatement,所有以 :paramName 形式出现的参数,都会被 Spring 替换为 JDBC 标准的 ? 占位符,并调用 setString()、setLong() 等类型安全方法绑定——恶意字符(如单引号、分号、--)根本进不了 SQL 解析阶段。
关键前提是:参数只出现在值位置,不能用于表名、列名、ORDER BY 字段或 GROUP BY 表达式。这些属于 SQL 结构,NamedParameterJdbcTemplate 不处理。
IN 查询必须用 MapSqlParameterSource 或 Map,不能拼字符串
原生 JdbcTemplate 不支持动态长度的 IN 列表,硬写 WHERE id IN (?, ?, ?) 要求你提前知道参数个数;而 NamedParameterJdbcTemplate 允许你传一个 List 作为整体参数,它会自动展开成对应数量的 ? 并一一绑定。
- ✅ 正确:
String sql = "SELECT * FROM user WHERE id IN :ids"; MapSqlParameterSource params = new MapSqlParameterSource("ids", Arrays.asList(1L, 2L, 3L)); - ❌ 错误:
"SELECT * FROM user WHERE id IN (" + String.join(",", ids) + ")"—— 直接触发注入 - ⚠️ 注意:
:ids是一个参数名,不是字符串插值;它的值必须是List类型,不能是逗号分隔的字符串
别把 :param 当模板变量用
NamedParameterJdbcTemplate 只绑定「值」,不解析 SQL 语法结构。如果你试图用它动态拼表名或排序字段,比如 "SELECT * FROM :table WHERE status = :status",运行时会直接报错:org.springframework.dao.InvalidDataAccessResourceUsageException: Parameter '@table' not found(实际错误更可能是 Parsing failed 或 Unknown column)。
- 表名、列名、
ORDER BY字段必须由白名单校验后硬编码,或通过StringUtils.replace()+ 预设枚举替换(例如sortField = "name".equals(input) ? "user_name" : "created_time") - 命名参数只接受
String、Number、Boolean、Date和List;传入Map或自定义对象需用BeanPropertySqlParameterSource
Spring Boot 3 默认已配好,但要注意 Bean 注入方式
Spring Boot 3 启动时自动注册了 NamedParameterJdbcTemplate Bean,通常只需 @Autowired 注入即可使用。如果手动 new 实例(比如在工具类里),必须传入已配置好的 JdbcTemplate 或 DataSource:
- ✅ 推荐:
@Autowired private NamedParameterJdbcTemplate namedParameterJdbcTemplate; - ⚠️ 风险操作:
new NamedParameterJdbcTemplate(jdbcTemplate)—— 容易漏掉事务上下文或连接池配置 - 注意:若项目用了多数据源,需确认注入的是目标库对应的
NamedParameterJdbcTemplateBean(加@Qualifier)
最常被忽略的一点:IN 查询中传空 List 会触发 Invalid argument value: java.io.NotSerializableException 或空结果集——这不是注入问题,但会让开发者误以为参数没生效。务必在调用前校验 list != null && !list.isEmpty()。

















