db2中?占位符仅在preparedstatement预编译上下文中有效,裸sql拼接或statement执行会报sql0104n;需严格匹配类型、显式处理null、xmlquery等扩展语法也须参数化绑定。

DB2 中 ? 占位符必须配合 PreparedStatement 使用,裸写 SQL 拼接无效
DB2 本身不解析或执行带 ? 的纯文本 SQL —— 它只在 JDBC(或 CLI/ODBC)的预编译上下文中识别参数标志符。如果你直接把 SELECT * FROM user WHERE id = ? 当作普通字符串传给 Statement.execute(),DB2 会报错 SQL0104N:遇到非法符号“?”。必须走 PreparedStatement 流程,让驱动在发送前完成参数绑定和类型推导。
常见错误现象:
- 用
Statement执行含?的 SQL,抛出SQL0104N - 手动字符串拼接
"WHERE name = '" + input + "'",哪怕 DB2 支持?也完全没意义 - 调用
setString(1, input)前未对input做空值或长度校验,导致业务逻辑异常而非安全问题
DB2 的 PreparedStatement 对参数类型敏感,NULL 和空字符串需显式处理
DB2 不像某些数据库能自动将空字符串映射为 NULL 或忽略类型差异。如果字段定义为 NOT NULL,而你调用 setString(1, ""),可能触发约束失败;若字段是 CHAR(10),传入 "abc" 会被右补空格,但传入 null 时必须用 setNull(1, Types.VARCHAR),不能只传 null。
实操建议:
- 对可能为空的字段,统一用
setNull(index, sqlType),不要依赖驱动猜测 - 字符型参数优先用
setString(),避免用setObject()引发隐式转换歧义 - 数值型字段如
DECIMAL(10,2),用setBigDecimal()而非setDouble(),防止精度丢失被绕过校验
存储过程中调用动态 SQL 时,PREPARE + EXECUTE IMMEDIATE 仍需参数化,不能拼接
有人误以为进到 DB2 存储过程里就“脱离应用层”,可以放心拼接字符串。实际上,EXECUTE IMMEDIATE 'SELECT * FROM t WHERE id = ' || v_id 和 Java 里拼接一样危险。DB2 支持在存储过程中使用 USING 子句绑定变量,例如:
PREPARE stmt FROM 'SELECT * FROM user WHERE status = ? AND created_time > ?'; EXECUTE stmt USING v_status, v_time;
注意点:
-
USING后只能跟变量名,不能是表达式(如v_status || '_active') - 变量必须在
DECLARE中明确定义类型,且与 SQL 中期望类型兼容 - 若需构建列名或表名(极少见),必须走白名单校验 +
QUOTES转义,不可参数化
DB2 的特殊语法(如 XMLQUERY、XMLEXISTS)容易漏掉参数化入口
当查询涉及 XML 字段时,开发者常把 XPath 表达式写死在 XMLQUERY 字符串里,例如:XMLQUERY('for $i in /root/item where $i/name = "xxx" return $i' PASSING doc)。这里的 "xxx" 是硬编码字符串,但如果它来自用户输入,就必须改用变量绑定:
XMLQUERY('for $i in /root/item where $i/name = $name return $i' PASSING doc, ? AS "name")
关键细节:
-
PASSING子句中用? AS "name"才算参数化,直接拼接字符串进 XPath 会失效 - XML 函数中的命名变量(如
$name)必须在PASSING中显式声明,否则报SQL0383N - DB2 11.5+ 支持
XMLCAST配合参数,但低版本需确保 XPath 里不出现用户可控内容
真正难防的不是不会用 ?,而是把参数化当成“加个问号”就完事——类型匹配、NULL 处理、动态上下文、扩展语法,每个环节都可能断链。别信“用了 PreparedStatement 就绝对安全”,得盯住每一条执行路径的实际绑定行为。











