JDBC 批量插入,返回 ID

Posted

技术标签:

【中文标题】JDBC 批量插入,返回 ID【英文标题】:JDBC Batch INSERT, RETURNING IDs 【发布时间】:2018-05-17 14:07:32 【问题描述】:

有没有办法使用 RETURNING INTO 获取受影响行的值? 我必须插入相同的行 x 次并获取插入行的 ID。

查询如下所示:

public static final String QUERY_FOR_SAVE =
        "DECLARE " +
           " resultId NUMBER ; " +
        "BEGIN " +
           " INSERT INTO x " +
           " (a, b, c, d, e, f, g, h, i, j, k, l, m)  " +
           " values (sequence.nextVal, :a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l) " +
           " RETURNING a INTO :resultId;" +
        "END;";

现在我可以在 JAVA 循环中使用 addBatch 将这个查询添加到批处理中

            IntStream.range(0, count)
                .forEach(index -> 
                    try 
                        setting parameters...
                        cs.addBatch();

                     catch (SQLException e) 
                        e.printStackTrace();
                    
                );
        cs.executeBatch();

有没有办法像这样从批处理中返回数组或列表? 我可以只使用 sql 执行这些插入 x 次,但在这种情况下,我也想知道如何返回一个 id 数组。

提前致谢

【问题讨论】:

【参考方案1】:

我假设这是关于 Oracle 的。据我所知,这是不可能的,但您可以在匿名 PL/SQL 块中使用 FORALL 运行批量插入,如我最近写的这篇文章中所述: https://blog.jooq.org/2018/05/02/how-to-run-a-bulk-insert-returning-statement-with-oracle-and-jdbc/

这是文章中的一个自包含 JDBC 示例,它插入一个值数组并将结果批量收集回 JDBC 客户端:

try (Connection con = DriverManager.getConnection(url, props);
    Statement s = con.createStatement();

    // The statement itself is much more simple as we can
    // use OUT parameters to collect results into, so no
    // auxiliary local variables and cursors are needed
    CallableStatement c = con.prepareCall(
        "DECLARE "
      + "  v_j t_j := ?; "
      + "BEGIN "
      + "  FORALL j IN 1 .. v_j.COUNT "
      + "    INSERT INTO x (j) VALUES (v_j(j)) "
      + "    RETURNING i, j, k "
      + "    BULK COLLECT INTO ?, ?, ?; "
      + "END;")) 

    try 

        // Create the table and the auxiliary types
        s.execute(
            "CREATE TABLE x ("
          + "  i INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,"
          + "  j VARCHAR2(50),"
          + "  k DATE DEFAULT SYSDATE"
          + ")");
        s.execute("CREATE TYPE t_i AS TABLE OF NUMBER(38)");
        s.execute("CREATE TYPE t_j AS TABLE OF VARCHAR2(50)");
        s.execute("CREATE TYPE t_k AS TABLE OF DATE");

        // Bind input and output arrays
        c.setArray(1, ((OracleConnection) con).createARRAY(
            "T_J", new String[]  "a", "b", "c" )
        );
        c.registerOutParameter(2, Types.ARRAY, "T_I");
        c.registerOutParameter(3, Types.ARRAY, "T_J");
        c.registerOutParameter(4, Types.ARRAY, "T_K");

        // Execute, fetch, and display output arrays
        c.execute();
        Object[] i = (Object[]) c.getArray(2).getArray();
        Object[] j = (Object[]) c.getArray(3).getArray();
        Object[] k = (Object[]) c.getArray(4).getArray();

        System.out.println(Arrays.asList(i));
        System.out.println(Arrays.asList(j));
        System.out.println(Arrays.asList(k));
    
    finally 
        try 
            s.execute("DROP TYPE t_i");
            s.execute("DROP TYPE t_j");
            s.execute("DROP TYPE t_k");
            s.execute("DROP TABLE x");
        
        catch (SQLException ignore) 
    

【讨论】:

是否需要创建辅助类型? @snowflake:在 Oracle 12.2 之前,我相信是的。 12.2以后可以使用DBMS_SQL中的类型(JDBC驱动会为你生成辅助类型)

以上是关于JDBC 批量插入,返回 ID的主要内容,如果未能解决你的问题,请参考以下文章

mybatis-plus解决 sqlserver批量插入list报错

mybatis-plus解决 sqlserver批量插入list报错

Java/Spring JDBC:批量插入 2 个表:从第 1 批插入获取第 2 个表所需的 FK ID

MariaDB jdbc 连接在批量插入期间失败

通过Mybatis执行拼接的SQL批量插入数据并返回数据库自增ID

通过Mybatis执行拼接的SQL批量插入数据并返回数据库自增ID