PLSQL JDBC:如何获取最后一行 ID?
Posted
技术标签:
【中文标题】PLSQL JDBC:如何获取最后一行 ID?【英文标题】:PLSQL JDBC: How to get last row ID? 【发布时间】:2011-04-02 21:48:54 【问题描述】:此 SQL 服务器 sn-p 的 PLSQL (Oracle) 等效项是什么?
BEGIN TRAN
INSERT INTO mytable(content) VALUES ("test") -- assume there's an ID column that is autoincrement
SELECT @@IDENTITY
COMMIT TRAN
在 C# 中,您可以调用 myCommand.ExecuteScalar() 来检索新行的 ID。
如何在 Oracle 中插入新行,并让 JDBC 获取新 id 的副本?
编辑: BalusC 提供了一个很好的起点。由于某种原因,JDBC 不喜欢命名参数绑定。这会给出“错误设置或注册的参数”SQLException。为什么会这样?
OracleConnection conn = getAppConnection();
String q = "BEGIN INSERT INTO tb (id) values (claim_seq.nextval) returning id into :newId; end;" ;
CallableStatement cs = (OracleCallableStatement) conn.prepareCall(q);
cs.registerOutParameter("newId", OracleTypes.NUMBER);
cs.execute();
int newId = cs.getInt("newId");
【问题讨论】:
选择id字段的最大值+1 ? @Aren -max(n)+1
不能扩展,也不能在多用户环境中工作。
【参考方案1】:
通常您会为此使用 Statement#getGeneratedKeys()
(另请参阅 this answer 的示例),但 Oracle JDBC 驱动程序目前(仍然)不支持。
您最好的选择是或者使用带有RETURNING
子句的CallableStatement
:
String sql = "BEGIN INSERT INTO mytable(id, content) VALUES (seq_mytable.NEXTVAL(), ?) RETURNING id INTO ?; END;";
Connection connection = null;
CallableStatement statement = null;
try
connection = database.getConnection();
statement = connection.prepareCall(sql);
statement.setString(1, "test");
statement.registerOutParameter(2, Types.NUMERIC);
statement.execute();
int id = statement.getInt(2);
// ...
或者在同一事务中在INSERT
之后触发SELECT sequencename.CURRVAL
:
String sql_insert = "INSERT INTO mytable(content) VALUES (?)";
String sql_currval = "SELECT seq_mytable.CURRVAL FROM dual";
Connection connection = null;
PreparedStatement statement = null;
Statement currvalStatement = null;
ResultSet currvalResultSet = null;
try
connection = database.getConnection();
connection.setAutoCommit(false);
statement = connection.prepareStatement(sql_insert);
statement.setString(1, "test");
statement.executeUpdate();
currvalStatement = connection.createStatement();
currvalResultSet = currvalStatement.executeQuery(sql_currval);
if (currvalResultSet.next())
int id = currvalResultSet.getInt(1);
connection.commit();
// ...
【讨论】:
您的意思是“SELECT seq_mytable.CURRVAL from dual”而不是“SELECT CURRVAL(seq_mytable)”? @Patrick:哦,drat,我想到了 PostgreSQL SQL 语法,我会更新(以前,PostgreSQL 曾经有同样的问题,不支持Statement#getGeneratedKeys()
,所以同样的“解决方法”是有必要,但大约一年前他们终于修复了他们的 JDBC 驱动程序以支持它)。
您好 BalusC,感谢您的帮助。你能看看我的编辑,看看你是否能解开另一个谜团?
您需要指定占位符索引,而不是列名。在registerOutParameter()
中将"newId"
替换为2
。
在实践中,我有一个带有 15 个参数的长插入语句,如果可以的话,我更喜欢命名绑定。如果我使用问号,JDBC 会抱怨有混合绑定。使用 out-parameter 时我必须使用序数绑定吗?【参考方案2】:
您可以使用 Oracle 的returning 子句。
insert into mytable(content) values ('test') returning your_id into :var;
查看this link 以获取代码示例。您需要 Oracle 10g 或更高版本,以及新版本的 JDBC 驱动程序。
【讨论】:
【参考方案3】:您可以使用 getGeneratedKeys(),通过显式选择关键字段。 这是一个sn-p:
// change the string to your connection string
Connection connection = DriverManager.getConnection("connection string");
// assume that the field "id" is PK, and PK-trigger exists
String sql = "insert into my_table(id) values (default)";
// you can select key field by field index
int[] colIdxes = 1 ;
// or by field name
String[] colNames = "id" ;
// Java 1.7 syntax; try-finally for older versions
try (PreparedStatement preparedStatement = connection.prepareStatement(sql, colNames))
// note: oracle JDBC driver do not support auto-generated key feature with batch update
// // insert 5 rows
// for (int i = 0; i < 5; i++)
//
// preparedStatement.addBatch();
//
//
// int[] batch = preparedStatement.executeBatch();
preparedStatement.executeUpdate();
// get generated keys
try (ResultSet resultSet = preparedStatement.getGeneratedKeys())
while (resultSet.next())
// assume that the key's type is BIGINT
long id = resultSet.getLong(1);
assertTrue(id != 0);
System.out.println(id);
详情请参考:http://docs.oracle.com/cd/E16655_01/java.121/e17657/jdbcvers.htm#CHDEGDHJ
【讨论】:
只要insert语句最多有7个参数,当参数超过7个时,就会抛出ArrayIndexOutofBound异常。看起来像 oracle jdbc 驱动程序有问题。 #Oracle_11g_R2。由于这个原因,BalusC 的回答更好。【参考方案4】:如果您使用 spring-jdbc
作为数据库,您可以考虑使用来自 morejdbc 的简洁包装器,它将如下所示:
import static org.morejdbc.SqlTypes.BIGINT;
import static org.morejdbc.JdbcCall.callSql;
import static org.morejdbc.*;
...
Out<Long> idOut = Out.of(BIGINT);
jdbcTemplate.execute(callSql("BEGIN INSERT INTO mytable(id, content) VALUES (seq_mytable.NEXTVAL(), ?) "
+ "RETURNING id INTO ?; END;")
.in(content)
.out(BIGINT, idOut));
System.out.println("Id is " + idOut.get());
如果你有类似的pojo
@lombok.Data
public class Entity
private long id;
private String content;
它可以更简洁:
Entity entity = ;
jdbcTemplate.execute(callSql("BEGIN INSERT INTO mytable(id, content) VALUES (seq_mytable.NEXTVAL(), ?) "
+ "RETURNING id INTO ?; END;")
.in(entity.getContent())
.out(BIGINT, entity::setId));
System.out.println("Id is " + entity.get());
【讨论】:
以上是关于PLSQL JDBC:如何获取最后一行 ID?的主要内容,如果未能解决你的问题,请参考以下文章