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?的主要内容,如果未能解决你的问题,请参考以下文章

将数据插入SQLite表后如何获取最后一行ID [重复]

如何获取每个 id 的特定行?

sql select语句,如何查找最后一行的数据

如何在第二级具有可变长度的MultiIndex DataFrame中获取第二级的所有最后一行

如何从表中获取最后一个插入 ID

PLSQL PHP:如何将大型 xml 数据从 PLSQL 函数获取到 PHP