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

5

我刚开始使用Oracle,所以我正在参考之前在这个SO问题中已经回答的内容。但是我似乎无法让它工作。这是我正在使用的语句:

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;

当我调用已经创建的PreparedStatement的executeQuery方法时,它可以将所有内容成功插入到数据库中。但是,我无法弄清如何检索ID。返回的ResultSet对象对我没有用。
if(resultSet.next()) ...

产生了一个难以处理的 SQLException,其内容为:
无法在 PLSQL 语句中执行获取操作:next
如何获取那个 lastId?显然我做错了什么。

你可以随时查询 SELECT FOO_ID_SEQ.CURRVAL FROM DUAL - Phil
2
发布函数或存储过程 - 需要知道是否将 lastid 设置为 INOUT 参数。 - OMG Ponies
1
CURRVAL是会话安全的,但RETURNING子句将允许你在一个语句中完成两件事情。 - OMG Ponies
@OMGPonies 一个初学Oracle开发者最大的“恍然大悟”时刻之一 ;) - Phil
你是将其作为匿名PLSQL块运行的吗?Java ResultSet对象IME需要Oracle返回SYS_REFCURSOR以遍历结果 - 这不是匿名PLSQL块的预期用途,它应该在存储过程中(最好是在包内)执行。 - OMG Ponies
显示剩余4条评论
6个回答

2

我不确定这个方法是否有效,因为我已经彻底清除了所有Oracle相关的内容,但是...

将你的声明更改为:

declare
  lastId OUT number;

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

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

1
这应该可以工作,尽管语句必须包含一个变量“?”:删除DECLARE部分,并将“RETURNING ID INTO lastId”替换为“RETURNING ID INTO ?”。此外,“getInt”返回一个整数 :) - Vincent Malgrat
谢谢 - 将 x 的类型从 byte 更改为 int。 - Jason Gritman

2

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

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);
}

2

将其变成一个返回值的函数(而不是过程)。或者,使用带有OUT参数的过程。


1

你可以使用 Statement.getGeneratedKeys() 来实现此功能。你只需要确保使用其中一个方法重载(例如这里的 Connection.prepareStatement 过载),告诉 JDBC 想要返回哪些列即可。

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语句即可。


1
long id = rS.getLong("id"); 是错误的 - 你必须使用列索引,而不是名称(对于Oracle)。 - Fisher
@Fisher:我绝对是在Oracle中使用这种技术(使用列名)。也许在某些情况/版本/配置下,它可能不起作用。 - ColinD

1

当您准备语句时,请将第二个参数设置为RETURN_GENERATED_KEYS。然后,您应该能够从语句对象中获取一个ResultSet


@James,可能是这样,但这似乎是一个奇怪的问题,因为您不应该让多个线程使用相同的键和语句。您能详细说明一下您的问题吗? - jzd
当您有一个单一的DAO实例(没有同步)并且有多个人在数据库上工作(某种服务器应用程序),这将产生不可靠的行为。我假设数据库按先到先得的原则返回此值,因此它是否适合单个用户应用程序?我想Hibernate的EntityManager使用代理模式或类似方法处理任何状态(持久化或非持久化)问题。 - James P.

1

你是在存储过程中执行这个操作吗?根据这个Oracle文档,使用服务器端驱动程序无法实现该功能。

Oracle服务器端内部驱动程序不支持检索自动生成的键特性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接