Oracle JDBC 执行 DDL 必须用 execute(),因 DDL 属非查询非更新操作,用 executeQuery() 或 executeUpdate() 会报 ORA-00900 或静默失败;且不支持 PreparedStatement 参数化,需校验后拼接表名/列名,并注意权限与隐式提交问题。
Oracle JDBC 执行 DDL 语句必须用 execute(),不能用 executeQuery() 或 executeUpdate()
oracle 的 create、alter、drop 等 ddl 语句在 jdbc 中属于“非查询非更新”的特殊操作,jdbc 规范要求必须用 execute() 方法执行,否则会抛出 sqlexception: ora-00900: invalid sql statement。这不是 oracle 驱动的 bug,而是 jdbc 对 ddl 的统一约定。
常见错误现象:
- 用
executeQuery()执行CREATE TABLE→ 报错ORA-00900 - 用
executeUpdate()执行GRANT SELECT ON ...→ 返回 -1 且不生效(某些驱动下甚至静默失败)
实操建议:
- 所有 DDL(含
CREATE、DROP、ALTER、GRANT、REVOKE)统一走Statement.execute(String sql) - 返回值
boolean可忽略:DDL 不产生结果集,也不影响行数,execute()总是返回false - 别试图用
PreparedStatement执行 DDL —— Oracle JDBC 不支持参数化 DDL,预编译会直接失败
动态建表时,表名/列名不能参数化,必须拼接字符串(但要防 SQL 注入)
Java 里没法把表名、列名当 ? 占位符传进 PreparedStatement,因为 DDL 语法不允许运行时替换标识符。所以得拼字符串,但拼接前必须严格校验输入。
使用场景:比如根据用户输入的业务类型生成日志表 log_<code>user_type。
立即学习“Java免费学习笔记(深入)”;
实操建议:
- 只允许字母、数字、下划线,且必须以字母或下划线开头 —— 用正则
^[a-zA-Z_][a-zA-Z0-9_]*$校验 - 禁止任何控制字符、双引号、点号、空格、分号 —— 这些可能被用于注入或截断语句
- 示例安全拼接:
String tableName = "log_" + userProvidedType.replaceAll("[^a-zA-Z0-9_]", "");(注意:仅作示意,实际应先校验再拼) - 不要用
String.format或+直接拼接未清洗的输入,哪怕只是“看起来可信”的内部参数
执行 DDL 需要对应权限,且权限不能通过 execute() 动态授予给当前连接用户
DDL 操作失败,十有八九是权限问题。比如 CREATE TABLE 要求用户有 CREATE TABLE 权限,而 GRANT 本身又需要 GRANT ANY PRIVILEGE 或对象所有者的授权能力。
常见错误现象:
-
ORA-01031: insufficient privileges—— 当前连接用户没权限建表 -
ORA-00942: table or view does not exist—— 在GRANT前尝试访问刚建的表,但 DDL 未提交(见下一条)
实操建议:
- JDBC 默认自动提交(
autoCommit=true),但 DDL 语句会隐式触发一次提交 —— 所以建表后立刻GRANT是可行的 - 如果关闭了自动提交(
conn.setAutoCommit(false)),DDL 会立即提交,打断当前事务 —— 别在事务块里混用 DDL 和 DML - 权限不是代码能绕过的:DBA 必须提前给应用用户授好
CREATE TABLE、CREATE SEQUENCE等最小必要权限,别指望 Java 里execute("GRANT ...")能给自己加权
Oracle 中 DDL 会隐式提交,无法回滚,且影响后续语句的 SCN 一致性
这是最容易被忽略的底层行为:Oracle 执行任意 DDL,都会强制 commit 当前事务,并推进系统变更号(SCN)。这意味着你不能在同一个事务里先改数据再建表,也不能对 DDL 做 rollback。
性能与兼容性影响:
- DDL 后所有未关闭的
ResultSet会失效,再次读取抛SQLException: Closed Connection或Invalid operation for read only resultset - 某些连接池(如 HikariCP)在 DDL 后可能因内部状态不一致导致后续获取连接变慢或报错
- 如果用 Spring
@Transactional包裹 DDL 操作,事务注解完全无效 —— DDL 自己已提交,Spring 不感知也不干预
实操建议:
- 把 DDL 操作单独抽成无事务方法,调用前确保连接已 commit 或 rollback 干净
- 执行 DDL 后,主动关闭之前打开的
ResultSet和Statement,避免复用旧资源 - 生产环境慎用动态建表 —— 除非是离线任务或租户隔离场景,否则 DDL 频繁会锁数据字典、拖慢整个实例










