使用 addBatch 和 executeBatch 时是不是必须使用相同的 prepareCall?为啥?

Posted

技术标签:

【中文标题】使用 addBatch 和 executeBatch 时是不是必须使用相同的 prepareCall?为啥?【英文标题】:Do I have to use the same prepareCall when using addBatch & executeBatch? Why?使用 addBatch 和 executeBatch 时是否必须使用相同的 prepareCall?为什么? 【发布时间】:2021-10-10 16:20:57 【问题描述】:

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

举例来说,这里有一些示例输入:

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 来根据最后一个参数的值创建记录。我知道我得到的错误是告诉我FKs 之一不存在。仅当调用了错误的 CALLs 集或调用了正确的 CALLs 集但其中一个 CALLs 失败时,才会发生这种情况。我确定这两个都不是问题,因为第一个循环按预期工作。

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

我认为这无关紧要,但为了更好的衡量标准:我使用的是 mysql 5.7。

【问题讨论】:

【参考方案1】:

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

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

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


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

【讨论】:

以上是关于使用 addBatch 和 executeBatch 时是不是必须使用相同的 prepareCall?为啥?的主要内容,如果未能解决你的问题,请参考以下文章

JDBC批量插入数据优化,使用addBatch和executeBatch

JDBC批量插入数据优化,使用addBatch和executeBatch

关于 addBatch(String) 的注意事项

java中的PreparedStatement.addBatch有啥限制吗?

statement.addBatch() /statement.executeBatch() 返回结果集?

java jdbc addBatch批处理不回滚