PreparedStatement 不支持动态拼装SQL,需用StringBuilder按条件拼接SQL片段并用?占位,参数通过setXxx绑定;IN子句需动态生成问号串,参数统一存List按序设置,注意空条件、NULL判断等边界处理。

PreparedStatement 本身不支持动态拼装 SQL,它的设计原则是SQL 结构固定、参数占位符动态绑定。所谓“动态 SQL”,实际是指根据业务条件灵活决定 WHERE 子句字段、ORDER BY、LIMIT 或 JOIN 等部分——这部分必须在执行前由 Java 代码拼出完整 SQL 字符串,再用该字符串创建 PreparedStatement,最后安全地设置参数。
拼装 SQL 字符串:用 StringBuilder 构建基础结构
根据运行时条件(如用户是否输入了姓名、年龄范围、状态筛选等),逐步追加 WHERE 条件。关键点是:只拼接 SQL 片段,不拼接参数值;所有值一律通过 ? 占位符 + setXxx() 绑定。
- 初始化 SQL 基础语句(例如 "SELECT * FROM user WHERE 1=1"),便于后续无脑追加 AND
- 每个条件单独判断,满足则 append(" AND name LIKE ?") 或 append(" AND age BETWEEN ? AND ?")
- 避免手拼字符串值(如 "name = '" + name + "'"),这是 SQL 注入根源
参数索引管理:用 List 存储参数值,按顺序 set
拼完 SQL 后,占位符 ? 的个数和顺序已确定。不要靠手动计数,推荐用 ArrayList 收集参数值,循环调用 setXxx:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 定义 List<Object> params = new ArrayList<>()
- 每追加一个条件,就 add() 对应参数(如 params.add("%"+name+"%"))
- 执行前遍历 params:for (int i = 0; i < params.size(); i++) { ps.setObject(i+1, params.get(i)); }
处理 IN 列表:用问号占位 + 动态生成 ? 串
IN (? , ? , ?) 的问号数量不确定,不能写死。需根据传入的 id 列表长度,动态拼出对应个数的 ?:
立即学习“Java免费学习笔记(深入)”;
- 假设 ids = Arrays.asList(101, 102, 103),则生成字符串 "(?, ?, ?)"
- SQL 拼为 "SELECT * FROM order WHERE status = ? AND user_id IN " + placeholders
- params 先 add(status),再addAll(ids),确保顺序与 ? 一一对应
注意边界情况:空条件、全选、NULL 判断
用户可能未填任何筛选项,此时 WHERE 1=1 会查全表,需确认是否允许;若字段允许 NULL,且需查 NULL 值,不能写 WHERE col = ?(NULL 无法用 = 判断),应拆成:
- col = ?(非 NULL 场景)
- col IS NULL(单独条件,不带 ?)
- 或统一用 COALESCE(col, 'NULL') = COALESCE(?, 'NULL')(慎用,可能失效索引)

















