使用 jdbi 进行更新时获取更新行的主键
Get primary keys of updated rows when doing an update with jdbi
我正在使用 jdbi(但如果需要,我会准备使用原始 jdbc)。我的数据库目前是 Oracle。我有一个更新,可以更新符合特定条件的第一行。我想从同一条语句中获取更新行的主键。这可能吗?
我试过了
Integer rowNum = handle
.createUpdate(sqlFindUpdate)
.bind("some var", myVal)
.executeAndReturnGeneratedKeys("id")
.mapTo(Integer.class)
.findOnly();
但我猜这不是生成的密钥,因为它没有找到它(非法状态异常,但更新成功)。
基本上,我在数据库中有一个我需要处理的项目列表。所以,我想获取下一个,同时标记为"in progress"。我希望能够支持多个工作线程,所以它需要是一个单一的语句 - 我不能在(状态已经改变所以它不再匹配)之后执行 select 并执行它before 引入了竞争条件。
我想我可以做一个使用 returning into
的存储过程,但我可以直接从 java 做吗?
我正在回答我自己的问题,但我认为这不是一个好的答案:) 我正在做的是一种混合。可以从 jdbi 动态 运行 PL/SQL 块。从技术上讲,这是我问过的 Java,而不是通过存储过程。但是,在我看来,这是一种 hack - 在这种情况下,为什么不只创建存储过程(如果我找不到更好的解决方案,我可能会这样做)。但是,对于信息,而不是:
String sql = "update foo set status = 1 where rownr in (select rownr from (select rownr from foo where runid = :runid and status = 0 order by rownr) where rownum = 1)";
return jdbi.withHandle((handle) -> {
handle
.createUpdate(sql)
.bind("runid", runId)
.executeAndReturnGeneratedKeys("rownr")
.mapTo(Integer.class)
.findOnly();
});
你可以做到
String sql = "declare\n" +
"vRownr foo.rownr%type;\n" +
"begin\n" +
"update foo set status = 1 where rownr in (select rownr from (select rownr from foo where runid = :runid and status = 0 order by rownr) where rownum = 1) returning rownr into vRownr;\n" +
":rownr := vRownr;\n" +
"end;";
return jdbi.withHandle((handle) -> {
OutParameters params = handle
.createCall(sql)
.bind("runid", runId)
.registerOutParameter("rownr", Types.INTEGER)
.invoke();
return params.getInt("rownr");
});
就像我说的,在这种情况下只创建过程可能更好,但如果需要,它确实让您可以选择在 java 中动态构建 SQL 我猜.
基于 this question,如评论中@APC 所链接,可以使用 OracleReturning
class 而无需 declare/begin/end.
String sql = "update foo set status = 1 where rownr in (select rownr from (select rownr from foo where runid = ? and status = 0 order by rownr) where rownum = 1) returning rownr into ?";
return jdbi.withHandle((handle) -> {
handle
.createUpdate(sql)
.bind(0, runId)
.addCustomizer(OracleReturning.returnParameters().register(1, OracleTypes.INTEGER))
.execute(OracleReturning.returningDml())
.mapTo(Integer.class)
.findOnly();
});
但是,OracleReturning 不支持命名参数,因此您必须使用位置参数。因为我使用 JDBI 而不是普通 JDBC 的主要原因是为了获得命名参数支持,这对我来说很重要,所以我不确定我会走哪条路
非常依赖于它是您正在调用的 Oracle 数据库...
更新:OracleReturning 中命名参数的增强已合并到 master,并将包含在 3.1.0 版本中。感谢@qualidafial 的补丁
我正在使用 jdbi(但如果需要,我会准备使用原始 jdbc)。我的数据库目前是 Oracle。我有一个更新,可以更新符合特定条件的第一行。我想从同一条语句中获取更新行的主键。这可能吗?
我试过了
Integer rowNum = handle
.createUpdate(sqlFindUpdate)
.bind("some var", myVal)
.executeAndReturnGeneratedKeys("id")
.mapTo(Integer.class)
.findOnly();
但我猜这不是生成的密钥,因为它没有找到它(非法状态异常,但更新成功)。
基本上,我在数据库中有一个我需要处理的项目列表。所以,我想获取下一个,同时标记为"in progress"。我希望能够支持多个工作线程,所以它需要是一个单一的语句 - 我不能在(状态已经改变所以它不再匹配)之后执行 select 并执行它before 引入了竞争条件。
我想我可以做一个使用 returning into
的存储过程,但我可以直接从 java 做吗?
我正在回答我自己的问题,但我认为这不是一个好的答案:) 我正在做的是一种混合。可以从 jdbi 动态 运行 PL/SQL 块。从技术上讲,这是我问过的 Java,而不是通过存储过程。但是,在我看来,这是一种 hack - 在这种情况下,为什么不只创建存储过程(如果我找不到更好的解决方案,我可能会这样做)。但是,对于信息,而不是:
String sql = "update foo set status = 1 where rownr in (select rownr from (select rownr from foo where runid = :runid and status = 0 order by rownr) where rownum = 1)";
return jdbi.withHandle((handle) -> {
handle
.createUpdate(sql)
.bind("runid", runId)
.executeAndReturnGeneratedKeys("rownr")
.mapTo(Integer.class)
.findOnly();
});
你可以做到
String sql = "declare\n" +
"vRownr foo.rownr%type;\n" +
"begin\n" +
"update foo set status = 1 where rownr in (select rownr from (select rownr from foo where runid = :runid and status = 0 order by rownr) where rownum = 1) returning rownr into vRownr;\n" +
":rownr := vRownr;\n" +
"end;";
return jdbi.withHandle((handle) -> {
OutParameters params = handle
.createCall(sql)
.bind("runid", runId)
.registerOutParameter("rownr", Types.INTEGER)
.invoke();
return params.getInt("rownr");
});
就像我说的,在这种情况下只创建过程可能更好,但如果需要,它确实让您可以选择在 java 中动态构建 SQL 我猜.
基于 this question,如评论中@APC 所链接,可以使用 OracleReturning
class 而无需 declare/begin/end.
String sql = "update foo set status = 1 where rownr in (select rownr from (select rownr from foo where runid = ? and status = 0 order by rownr) where rownum = 1) returning rownr into ?";
return jdbi.withHandle((handle) -> {
handle
.createUpdate(sql)
.bind(0, runId)
.addCustomizer(OracleReturning.returnParameters().register(1, OracleTypes.INTEGER))
.execute(OracleReturning.returningDml())
.mapTo(Integer.class)
.findOnly();
});
但是,OracleReturning 不支持命名参数,因此您必须使用位置参数。因为我使用 JDBI 而不是普通 JDBC 的主要原因是为了获得命名参数支持,这对我来说很重要,所以我不确定我会走哪条路
非常依赖于它是您正在调用的 Oracle 数据库...
更新:OracleReturning 中命名参数的增强已合并到 master,并将包含在 3.1.0 版本中。感谢@qualidafial 的补丁