使用 addBatch 和 executeBatch 时是否必须使用相同的 prepareCall?为什么?

Do I have to use the same prepareCall when using addBatch & executeBatch? Why?

我是 JDBC 的新手,正在学习如何使用批处理。在浪费了很多时间试图找出为什么我的 JavaScript 和 SQL 存储过程的组合不起作用之后,我想我知道你必须在使用 prepareCall 时使用 addBatchexecuteBatch。这是真的?如果是,为什么?

为了举例说明,这里有一些示例输入:

var vals = [["value1_1","value2_1","value3","value4","value5","value6_1"],
    ["value1_2","value2_2","value3","value4","value5","value6_2"]]  

下面的循环按预期工作。注意我在进入循环之前prepareCall.

params = Array(vals.length).fill("?") 
pstmt = conn.prepareCall("{call "+storedProcedure+"("+params+")}");

for (var i = 0; i < vals.length; i++) { // for each array

    for (var j = 0; j < vals[i].length; j++) { // for each value within each array
        // set the string
        pstmt.setString(j+1, vals[i][j]); 
    }
    
    pstmt.addBatch();
  } 

try {

    pstmt.executeBatch();
    
  } catch (err) {
    //my err msg code
    pstmt.close();
    conn.close();
} 

现在,有时我的记录具有不同数量的参数,所以我想我可以将 prepareCall 移动到第一个循环中,这样我就可以根据需要为每个输入数组更改参数数量.

for (var i = 0; i < vals.length; i++) { // for each array
    // moved prepareCall here
    params = Array(vals.length).fill("?") 
    pstmt = conn.prepareCall("{call "+storedProcedure+"("+params+")}");

    for (var j = 0; j < vals[i].length; j++) { // for each value within each array
        // set the string
        pstmt.setString(j+1, vals[i][j]); 
    }
    
    pstmt.addBatch();
  } 

try {

    pstmt.executeBatch();
    
  } catch (err) {
    //my err msg code
    pstmt.close();
    conn.close();
} 

对于第二个循环,我没有从 Javascript 中得到任何错误,但是我从我的存储过程中得到一个外部约束错误。我的存储过程制作了一组 CALLs 以根据最后一个参数的值创建记录。我知道我得到的错误是告诉我 FK 之一不存在。仅当调用了错误的 CALL 集或调用了正确的 CALL 集但其中一个 CALL 失败时,才会发生这种情况。我确定这都不是问题,因为第一个循环按预期工作。

因此,我有兴趣了解为什么在 executeBatch 时必须使用一个 prepareCall?确认一下,当我用不同数量的参数调用不同的存储过程时,我必须使用完全独立的 prepareCalls?

我认为这不重要,但为了更好的衡量:我正在使用 MySQL 5.7.

方法 prepareCall returns 为您传递给 prepareCall 的特定语句编译的 CallableStatement 对象。当您调用 addBatch 时,参数集将添加到 CallableStatement 的特定实例中。如果你再次执行 prepareCall,你会得到一个 不同的 CallableStatement 句柄,有自己的批次。

因此,在外循环每次迭代的第二段代码中,您创建了一个 new CallableStatement,丢失了先前的可调用语句及其批处理 (在您的代码中以及可能在您的 DBMS 中创建内存泄漏)。因此,当您调用 executeBatch 时,您只会执行您准备的 last 语句,并且只有一组参数值。

如果需要执行不同的语句文本,则需要按顺序准备和执行,不能使用batch(*)。


* - 如果多组参数使用相同的语句文本,您可以使用批处理,但如果不同语句文本之间存在顺序依赖性,这可能会变得棘手