命名参数未调用 setparameter() 将导致 queryparameterexception 且功能失效;in 子句必须用 setparameterlist();表名、字段名、排序方向不可参数化,须白名单校验;优先使用 criteria api 防注入。

命名参数写了但没调用 setParameter() 就等于没防注入
HQL 里写 :name 只是声明占位符,Hibernate 在 createQuery() 阶段完全不检查参数是否绑定。漏掉 query.setParameter("name", value),运行时直接抛 QueryParameterException,查不到数据——这不是注入,但功能当场失效。
常见踩坑点:
-
setParameter("userName", input)和 HQL 中的:username大小写不一致 → 报错 - 拼错参数名,比如 HQL 写
:user_name却传"username"→ 同样报错 - 只创建 Query,忘了后续绑定动作 → 查询逻辑中断,且没有任何编译期提示
IN 子句必须用 setParameterList(),不能塞进 setParameter()
把 List<long></long> 直接传给 setParameter("ids", idList) 是常见错误。Hibernate 会把这个 List 当作单个参数值处理,生成类似 WHERE id = ?(? 对应整个 List 对象),导致语法错误或全表扫描。
正确做法只有这一种:
- 用
setParameterList("ids", idList),Hibernate 才会自动展开成WHERE id IN (?, ?, ?) - 别手动拼
"IN (" + String.join(",", ids) + ")"→ 注入风险+类型转换问题 - 空列表要单独判断,
setParameterList()不接受空集合,会抛异常
表名、字段名、排序方向无法参数化,必须走白名单校验
HQL 和原生 SQL 都不支持把 FROM 后的表名、ORDER BY 后的字段、ASC/DESC 这类结构当参数绑定。任何尝试字符串拼接的写法都是非法且危险的:
-
createQuery("FROM " + tableName + " WHERE id = :id")→ ❌ 表名拼接不可控 -
createQuery("ORDER BY " + sortField + " " + sortOrder)→ ❌ 排序方向若来自用户输入,可能执行ASC; DROP TABLE user; - 白名单硬性校验示例:
if (!Set.of("user", "order", "product").contains(tableName)) throw new IllegalArgumentException(); - 排序方向只允许字面量
"ASC"或"DESC",禁止动态传入
优先用 Criteria API,而不是手写 HQL
Critera API 是唯一能从源头杜绝 HQL 注入的方式:整个查询由 Java 方法链构建,没有字符串参与拼接,字段名是 Java 属性名,拼错直接编译失败。
适用边界很明确:
- 简单条件、关联查询、类型安全场景 →
CriteriaBuilder是首选 - 需要
GROUP_CONCAT、复杂子查询嵌套、数据库特有函数时 → 回退到 HQL,但必须严格参数绑定 - 原生 SQL 查询(
createSQLQuery())只能用?占位符 + 位置绑定,不支持命名参数
真正容易被忽略的是动态结构——不是主查询写错了,而是 WHERE 条件里拼了字段名、IN 列表没走 setParameterList、排序方向没校验。这些地方不设防,再规范的 HQL 也白搭。











