【问题标题】:Getting id after insert within a transaction (Oracle)在事务中插入后获取 id (Oracle)
【发布时间】:2013-07-01 19:09:47
【问题描述】:

假设我有三个表:team、player、team_player。表 team_player 是一个允许“多对多”关系的桥接表。

当有人想要创建一个新团队时,他们会指定该团队的初始玩家。

如何在同一事务中同时插入 team 和 team_player 行?也就是说,我想在提交到新的团队行之前插入所有 team_player 记录。我正在使用 JDBC 和 Oracle。

当我尝试下面的代码时,即使 team.id 是一个数字(由触发器递增),teamId 也会填充一串字母。所以,这似乎不是我刚刚尝试插入的记录的 ID(但尚未提交)。

c = DB.getConnection();
c.setAutoCommit(false);

sql = "INSERT INTO team (name) values (?)";
myInsert = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
myInsert.setString(1, "cougars");
int affectedRows = memoInsert.executeUpdate();

String teamId;
ResultSet generatedKeys = myInsert.getGeneratedKeys();
if (generatedKeys.next()) {
    teamId = generatedKeys.getString(1);
}

// ...loop through players inserting each player and team.id into team_player

// c.commit();

这是我读到 RETURN_GENERATED_KEYS 的地方: How to get the insert ID in JDBC?

【问题讨论】:

  • Oracle 有序列。它们在这种情况下工作得很好。

标签: java sql oracle jdbc


【解决方案1】:

请参考Statement.getGeneratedKeys() - 它会返回一个生成密钥的结果集。我相信这就是您所寻求的。

基本上,Spring jdbc 使用这种方法来检索生成的 id (example from JdbcTemplate class)

一个不太优雅的解决方案是使用 Oracle 的 RETURNING clause ,但您必须将 insert 包装到存储的 proc 中才能取回 id

【讨论】:

  • 是的,我在问题的代码示例中使用了 getGeneratedKeys()。问题是它返回的不是 id。它是一串字母。我的 id 字段是一个数字。
  • 它返回给你 ROWID 。 getGeneratedKeys 文档说,如果未指定表示 id 的列,则驱动程序会为您决定 - 显然,对于 Oracle,它将是 ROWID
【解决方案2】:

Oracle JDBC 驱动程序不支持getGeneratedKeys() - 您在触发器中手动生成键,可能来自SEQUENCE

可以使用Oracle的returning子句:

String query = "BEGIN INSERT INTO team (name) values (?) returning id into ?; END;";
CallableStatement cs = conn.prepareCall(query);
cs.setString(1, "cougars");
cs.registerOutParameter(2, OracleTypes.NUMBER);
cs.execute();
System.out.println(cs.getInt(2));

或者用第二个 SQL 查询获取最后一个序列号:

SELECT mysequence.CURRVAL FROM dual

【讨论】:

  • 感谢您的帮助;我应该用变量名替换 :var 吗?如果我保留 ":var" 原样,我会收到有关缺少 IN 或 OUT 参数的错误。如果我用“shinyNewId”替换“:var”,它会抱怨参数类型冲突。我会继续戳它。
  • 我有一些明显的错误,答案已编辑 - 希望它现在可以工作。
  • 这行得通。只是出于好奇......我避免了你给我的选项#2,因为我不完全理解它。如果我进行第二次查询,如果其他人在我的两次查询之间点击该序列,那会不会出错?无论如何,再次感谢。
  • CURVAL 返回当前会话的最后一个序列值,因此您描述的问题不会发生。
  • 如果列由触发器填充,Oracle JDBC 驱动程序确实支持getGeneratedKeys()
【解决方案3】:

您需要告诉驱动程序返回哪个列。

如果您的 ID 由触发器填充,则以下操作将起作用:

sql = "INSERT INTO team (name) values (?)";

// pass an array of column names to be returned by the driver instead of the int value
// this assumes the column is named ID (I think it has to be all uppercase)

myInsert = c.prepareStatement(sql, new String[]{"ID"});

myInsert.setString(1, "cougars");
int affectedRows = memoInsert.executeUpdate();

String teamId;
ResultSet generatedKeys = myInsert.getGeneratedKeys();
if (generatedKeys.next()) {
    teamId = generatedKeys.getString(1);
}

【讨论】:

  • 像魅力一样工作!谢谢!
  • 如果 getGeneratedKeys() 由于连接丢失而失败怎么办。插入的记录将如何回滚?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-25
  • 2019-10-28
  • 1970-01-01
  • 2011-03-09
  • 1970-01-01
  • 2011-03-16
相关资源
最近更新 更多