使用 JDBC 获取 Oracle 11g 的最后插入 ID

Posted

技术标签:

【中文标题】使用 JDBC 获取 Oracle 11g 的最后插入 ID【英文标题】:Get last insert id with Oracle 11g using JDBC 【发布时间】:2011-06-24 13:08:33 【问题描述】:

我是使用 Oracle 的新手,所以我将放弃之前在 this SO question 中已经回答的内容。我似乎无法让它工作。这是我正在使用的语句:

declare
  lastId number;
begin
INSERT INTO "DB_OWNER"."FOO" 
  (ID, DEPARTMENT, BUSINESS)
  VALUES (FOO_ID_SEQ.NEXTVAL, 'Database Management', 'Oracle')
  RETURNING ID INTO lastId;
end;

当我调用 executeQuery 我所做的 PreparedStatement 时,它会将所有内容都插入数据库中。但是,我似乎无法弄清楚如何检索 ID。返回的 ResultSet 对象对我不起作用。调用

if(resultSet.next()) ...

产生一个讨厌的 SQLException,内容如下:

无法对 PLSQL 语句执行提取:下一个

我如何获得lastId?显然我做错了。

【问题讨论】:

您可以随时查询SELECT FOO_ID_SEQ.CURRVAL FROM DUAL 发布函数或存储过程 -- 需要知道是否将lastid设置为INOUT参数。 如果我必须执行另一个查询,我不能保证它是我刚刚插入的元素的 id。那里可能还有另一个查询。 CURRVAL 是会话安全的,但 RETURNING 子句允许您在一个语句中做两件事。 @OMGPonies 崭露头角的 Oracle 开发人员可以体验到的最大“灯泡”时刻之一 ;) 【参考方案1】:

使它成为一个返回给你的函数(而不是一个过程)。或者,有一个带有 OUT 参数的过程。

【讨论】:

【参考方案2】:

不确定这是否可行,因为我已经清除了所有计算机中的任何 Oracle,但是...

将您的声明更改为:

declare
  lastId OUT number;

通过在您的连接上使用 prepareCall() 将您的语句从 PreparedStatement 切换到 CallableStatement。然后在调用前注册输出参数,更新后读取:

cstmt.registerOutParameter(1, java.sql.Types.NUMERIC);
cstmt.executeUpdate();
int x = cstmt.getInt(1);

【讨论】:

这应该可以,尽管语句必须包含变量?:删除 DECLARE 部分,并将@​​987654324@ 替换为RETURNING ID INTO ?getInt 也返回一个 int :) 谢谢 - 将 x 的类型从 byte 更改为 int。【参考方案3】:

我尝试使用 Oracle 驱动程序 v11.2.0.3.0(因为 10.x 和 11.1.x 中存在一些错误,请参阅 other blog)。以下代码工作正常:

final String sql = "insert into TABLE(SOME_COL, OTHER_COL) values (?, ?)";
PreparedStatement ps = con.prepareStatement(sql, new String[] "ID");
ps.setLong(1, 264);
ps.setLong(2, 1);
int executeUpdate = ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next() ) 
    // The generated id
    long id = rs.getLong(1);
    System.out.println("executeUpdate: " + executeUpdate + ", id: " + id);

【讨论】:

【参考方案4】:

准备语句时,将第二个参数设置为RETURN_GENERATED_KEYS。然后你应该能够从语句对象中得到一个ResultSet

【讨论】:

@James,可能,但这似乎是一个奇怪的问题,因为您真的不应该让多个线程使用相同的键和语句。你能扩展你的问题吗? 当然。如果您有一个 DAO 实例(未同步)并且有多个人在处理数据库(某种服务器应用程序),这将产生不可靠的行为。我假设数据库以先到先得的方式返回此值,那么它是否适合单用户应用程序?我想 Hibernate 的 EntityManager 使用代理模式或类似的方式处理状态(持久或不持久)的任何问题。【参考方案5】:

您可以使用Statement.getGeneratedKeys() 来执行此操作。您只需要确保使用其中一种方法重载来告诉 JDBC 您想要返回哪些列,例如此处的 Connection.prepareStatement 重载:

Connection conn = ...
PreparedStatement pS = conn.prepareStatement(sql, new String[]"id");
pS.executeUpdate();
ResultSet rS = pS.getGeneratedKeys();
if (rS.next()) 
  long id = rS.getLong("id");
  ...

你不需要用这个做RETURNING x INTO的东西,只需使用你想要的基本SQL语句。

【讨论】:

long id = rS.getLong("id");错了 - 您必须使用列索引,而不是名称(使用 oracle) @Fisher:我绝对是在 Oracle 中使用这种技术(使用列名)。也许在 Oracle 的某些情况/版本/配置中它不起作用。【参考方案6】:

你是在存储过程中这样做的吗?根据这个Oracle document,它不适用于服务器端驱动程序。

Oracle服务器端内部驱动不支持
检索自动生成的密钥功能。

【讨论】:

以上是关于使用 JDBC 获取 Oracle 11g 的最后插入 ID的主要内容,如果未能解决你的问题,请参考以下文章

oracle 11g resultSet如何获取表名

oracle 11g - 在oracle 11g数据库中获取标识列的最后一个插入ID的方法是什么?

转: Maven 仓库中添加Oracle JDBC驱动(11g)

jdbc连接oracle11g出现ora-12505错误怎么解决?

在Maven仓库中手动添加Oracle11g JDBC驱动

jdbc连接oracle11g的问题——查不出来数据,权限问题