PreparedStatement 用 ? 占位动态值并用 setXxx() 按索引赋值,支持 WHERE/INSERT/UPDATE 中的数据内容;表名、列名等标识符不可占位,须白名单校验后拼接;IN 参数需动态生成 ? 占位符再逐个设置。

PreparedStatement 设置动态参数,核心是用占位符 ? 替代 SQL 中的可变值,再通过 setXxx() 方法按索引(从 1 开始)逐个赋值。它不支持直接拼接表名、列名或 SQL 关键字——这些必须在预编译前确定,不能动态化。
✅ 正确设置值类型的动态参数
所有 WHERE 条件、INSERT 值、UPDATE 赋值等「数据内容」都可以用 ? 占位,并调用对应类型的方法:
-
setString(1, "张三")—— 字符串(自动加引号,无需手动处理) -
setInt(2, 25)—— 整数 -
setDate(3, Date.valueOf("2024-01-01"))—— 日期 -
setNull(4, Types.VARCHAR)—— 显式设为 NULL(需指定 SQL 类型) -
setBoolean(5, true)、setDouble(6, 99.5)等同理
❌ 表名、列名、排序字段不能用 ? 占位
下面写法是错误且会报错的:
String sql = "SELECT * FROM ? WHERE ? = ?";
ps.setString(1, tableName); // ❌ 不合法!
立即学习“Java免费学习笔记(深入)”;
原因:PreparedStatement 的预编译阶段需要明确表结构和语法结构,? 只能代表「值」,不能代表「标识符」或「语法单元」。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
替代方案:
– 若需动态表名/列名,必须用字符串拼接(但要严格校验白名单,防止 SQL 注入);
– 例如:只允许 tableName 属于 {"user", "order", "product"} 才拼入 SQL。
✅ 处理不确定数量的 IN 参数(如 id IN (?, ?, ?))
IN 子句中的参数个数不确定时,不能写死 ? 个数,需动态生成占位符:
- 先根据参数列表长度构造 SQL,如 3 个 ID →
"WHERE id IN (?, ?, ?)" - 再循环调用
setXxx(),索引从 1 开始递增
示例代码片段:
List<Integer> ids = Arrays.asList(101, 102, 105);
String placeholders = String.join(",", Collections.nCopies(ids.size(), "?"));
String sql = "SELECT * FROM user WHERE id IN (" + placeholders + ")";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (int i = 0; i ps.setInt(i + 1, ids.get(i)); // 索引从 1 开始
}
// 执行查询
}
✅ 注意空值与类型匹配
数据库字段类型和 set 方法类型需尽量一致,否则可能隐式转换失败或精度丢失:
- 数据库是
DECIMAL(10,2)→ 推荐用setBigDecimal(),而非setDouble() - 字段允许 NULL → 传
null时务必用setNull(index, sqlType),不能直接setString(i, null) - 时间字段是
TIMESTAMP→ 用setTimestamp(),配合java.sql.Timestamp

















