【问题标题】:Get last insert id with Oracle 11g using JDBC使用 JDBC 获取 Oracle 11g 的最后插入 ID
【发布时间】: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 开发人员可以体验到的最大“灯泡”时刻之一 ;)

标签: java oracle jdbc oracle11g


【解决方案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服务器端内部驱动不支持
      检索自动生成的密钥功能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-29
        • 1970-01-01
        • 2013-07-28
        • 1970-01-01
        • 2019-10-28
        • 2016-11-25
        • 1970-01-01
        • 2011-05-13
        相关资源
        最近更新 更多