在动态 SQL(MyBatis、JDBC、Spring Data 等场景都常见)中,“排序”通常是不能简单用 ? 占位符的,因为 ORDER BY 后面跟的是列名和方向,属于 SQL 结构,而不是参数。
下面以最常见的 MyBatis 为例,讲几种正确实现方式。
${}(必须白名单校验)<select id="list" resultType="User">
SELECT * FROM user
ORDER BY ${sortField} ${sortOrder}
</select>
调用:
map.put("sortField", "create_time");
map.put("sortOrder", "DESC");
✅ 优点:简单直观
❌ 风险:${} 会直接拼接 SQL,容易被注入
private static final Set<String> ALLOW_FIELDS =
Set.of("id", "name", "create_time");
private static final Set<String> ALLOW_ORDERS =
Set.of("ASC", "DESC");
if (!ALLOW_FIELDS.contains(sortField)) {
sortField = "id";
}
if (!ALLOW_ORDERS.contains(sortOrder)) {
sortOrder = "ASC";
}
<choose> 标签(最安全)<select id="list" resultType="User">
SELECT * FROM user
ORDER BY
<choose>
<when test="sortField == 'name'">name</when>
<when test="sortField == 'age'">age</when>
<otherwise>id</otherwise>
</choose>
<choose>
<when test="sortOrder == 'DESC'">DESC</when>
<otherwise>ASC</otherwise>
</choose>
</select>
✅ 完全防止 SQL 注入
✅ 适合字段较少的情况
<select id="list" resultType="User">
SELECT * FROM user
<where>
<if test="status != null">AND status = #{status}</if>
</where>
ORDER BY
<foreach collection="sortList" item="s" separator=",">
${s.field} ${s.order}
</foreach>
</select>
Java:
List<Map<String,String>> sortList = List.of(
Map.of("field","create_time","order","DESC"),
Map.of("field","id","order","ASC")
);
⚠️ 同样需要白名单校验
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("create_time");
wrapper.orderByAsc("id");
或前端传参:
wrapper.orderBy(
true,
"DESC".equals(order),
sortField
);
✅ 自动防注入
✅ 代码清晰
| 场景 | 推荐方式 |
|---|---|
| 字段少 | <choose> |
| 字段多 | ${} + 白名单 |
| MyBatis-Plus | QueryWrapper |
| 绝对安全 | 不拼 SQL,用枚举 |
如果你用的是 MyBatis / JPA / JDBC / 原生 SQL,可以告诉我,我可以给你对应示例。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。