oracle merge语句在jdbc中必须使用preparedstatement执行,不可用statement;需严格匹配on条件字段类型与setxxx()方法,returning子句须registeroutparameter,批量操作应复用preparedstatement并调用addbatch/executebatch。

Oracle MERGE 语句在 JDBC 中必须用 PreparedStatement 执行
Oracle 的 MERGE 是 DML 语句,不能用 Statement.execute() 直接执行(会报 ORA-00900: invalid SQL statement)。JDBC 要求所有含绑定变量的 DML 必须走 PreparedStatement,即使你没写 ? —— 因为 Oracle 内部解析 MERGE 时依赖预编译上下文。
常见错误现象:java.sql.SQLException: ORA-00900 或 executeUpdate() returns 0 unexpectedly(其实是没真正执行,而是语法校验失败)。
- 必须用
connection.prepareStatement(...),不能用connection.createStatement() - SQL 字符串里可以不含
?,但调用时仍要走preparedStatement.executeUpdate() - 如果 MERGE 里有子查询或引用序列(如
seq.nextval),确保目标表权限和序列访问权限已授予当前用户
注意 ON 子句字段类型与 Java 绑定值严格匹配
Oracle 对 MERGE ... ON (a = ?) 的类型推断很敏感。比如数据库字段是 NUMBER(10,0),但你用 setString(1, "123"),可能触发隐式转换失败或索引失效;若字段是 VARCHAR2(50) 却传入 setInt(1, 123),则报 ORA-01722: invalid number。
使用场景:当 MERGE 的 ON 条件涉及主键或唯一约束字段时,类型错配会导致整条语句不匹配任何行,INSERT 分支被误触发,或 UPDATE 完全跳过。
- 查清目标字段的 DDL 类型(用
DESC table_name或查询USER_TAB_COLUMNS) - 用对应
setXXX()方法:setLong()对NUMBER,setString()对VARCHAR2,setTimestamp()对TIMESTAMP - 避免用
setObject()自动推断 —— Oracle JDBC 驱动版本不同,行为可能不一致
RETURNING 子句需配合 executeUpdate() + getGeneratedKeys() 或 registerOutParameter
如果 MERGE 后需要获取被插入/更新行的主键(例如 id),不能靠 executeQuery(),也不能直接读 ResultSet。Oracle 的 RETURNING 必须显式注册输出参数或通过特殊方式提取。
示例 SQL:MERGE INTO t USING DUAL ON (t.code = ?) WHEN NOT MATCHED THEN INSERT (id, code) VALUES (seq.nextval, ?) RETURNING id INTO ?
- 必须用
PreparedStatement,且第 3 个参数(?)要调用registerOutParameter(3, Types.NUMERIC) - 执行完
executeUpdate()后,用getLong(3)或getInt(3)获取返回值 - 不支持标准 JDBC 的
getGeneratedKeys()—— Oracle 的 RETURNING 和 MySQL/Auto-increment 机制完全不同 - 注意:RETURNING 只能返回单行结果;多行 MERGE 时,只返回最后一条生效操作的结果
批量 MERGE 推荐拆成单条 + 批处理,而非拼接多条 MERGE
有人试图用字符串拼接多个 MERGE 语句(用分号隔开),再交给 execute() 执行 —— 这在 Oracle JDBC 中完全无效,会报 ORA-00911: invalid character。JDBC 不支持批处理多条独立 DML(除非用 Oracle 特有的 addBatch() + executeBatch(),但仅限同构 SQL)。
正确做法是复用同一个 PreparedStatement 实例,循环设置参数并调用 addBatch():
String sql = "MERGE INTO users u USING DUAL ON (u.uid = ?) WHEN MATCHED THEN UPDATE SET name = ? WHEN NOT MATCHED THEN INSERT (uid, name) VALUES (?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
for (User user : users) {
ps.setString(1, user.getUid());
ps.setString(2, user.getName());
ps.setString(3, user.getUid());
ps.setString(4, user.getName());
ps.addBatch();
}
ps.executeBatch(); // 注意:需开启 Oracle 批处理支持(默认开启)
性能影响:批量 MERGE 比逐条快 3–10 倍,但要注意 batchSize 控制在 1000 以内,否则容易触发 Oracle 的 PGA 内存限制或锁等待。
容易被忽略的是:每条 MERGE 的 ON 条件必须能独立判断匹配,不能依赖前一条执行结果 —— Oracle 批处理中各条语句仍是隔离执行的。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











