【问题标题】:Using Prepared Statement multiple times efficiently有效地多次使用 Prepared Statement
【发布时间】:2012-08-19 11:53:33
【问题描述】:

下面是我使用 Prepared Statement 在 Oracle 数据库中插入 multiple records( around 5000-7000) 的代码。

我目前的做法好吗?或者可以使用一些batch thing 进行更多改进?

pstatement = db_connection.prepareStatement(PDSLnPConstants.UPSERT_SQL);

for (Entry<Integer, LinkedHashMap<Integer, String>> entry : MAPPING.entrySet()) {

    pstatement.setInt(1, entry.getKey());
    pstatement.setString(2, entry.getValue().get(LnPConstants.CGUID_ID));
    pstatement.setString(3, entry.getValue().get(LnPConstants.PGUID_ID));
    pstatement.setString(4, entry.getValue().get(LnPConstants.SGUID_ID));
    pstatement.setString(5, entry.getValue().get(LnPConstants.UID_ID));
    pstatement.setString(6, entry.getValue().get(LnPConstants.ULOC_ID));
    pstatement.setString(7, entry.getValue().get(LnPConstants.SLOC_ID));
    pstatement.setString(8, entry.getValue().get(LnPConstants.PLOC_ID));
    pstatement.setString(9, entry.getValue().get(LnPConstants.ALOC_ID));
    pstatement.setString(10, entry.getValue().get(LnPConstants.SITE_ID));
    pstatement.executeUpdate();

    pstatement.clearParameters();
}

我正在使用的更新代码:-

public void runNextCommand() {

    Connection db_connection = null;
    PreparedStatement pstatement = null;
    int batchLimit = 1000;
    boolean autoCommit = false;

    try {
        db_connection = getDBConnection();

        autoCommit = db_connection.getAutoCommit();
        db_connection.setAutoCommit(false); //Turn off autoCommit
        pstatement = db_connection.prepareStatement(LnPConstants.UPSERT_SQL); // create a statement

        for (Entry<Integer, LinkedHashMap<Integer, String>> entry : GUID_ID_MAPPING.entrySet()) {
            pstatement.setInt(1, entry.getKey());
            pstatement.setString(2, entry.getValue().get(LnPConstants.CGUID_ID));
            pstatement.setString(3, entry.getValue().get(LnPConstants.PGUID_ID));
            pstatement.setString(4, entry.getValue().get(LnPConstants.SGUID_ID));
            pstatement.setString(5, entry.getValue().get(LnPConstants.UID_ID));
            pstatement.setString(6, entry.getValue().get(LnPConstants.ULOC_ID));
            pstatement.setString(7, entry.getValue().get(LnPConstants.SLOC_ID));
            pstatement.setString(8, entry.getValue().get(LnPConstants.PLOC_ID));
            pstatement.setString(9, entry.getValue().get(LnPConstants.ALOC_ID));
            pstatement.setString(10, entry.getValue().get(LnPConstants.SITE_ID));
            pstatement.addBatch();

            batchLimit--;

            if(batchLimit == 0){
                pstatement.executeBatch();
                pstatement.clearBatch();
                batchLimit = 1000;
            }
            pstatement.clearParameters();
        }

    } catch (SQLException e) {
        getLogger().log(LogLevel.ERROR, e);
    } finally {
        try {
            pstatement.executeBatch();
            db_connection.commit();
            db_connection.setAutoCommit(autoCommit);
        } catch (SQLException e1) {
            getLogger().log(LogLevel.ERROR, e1.getMessage(), e1.fillInStackTrace());
        }

        if (pstatement  != null) {
            try {
                pstatement.close();
                pstatement = null;
            } catch (SQLException e) {
                getLogger().log(LogLevel.ERROR, e.getMessage(), e.fillInStackTrace());
            }
        }
        if (db_connection!= null) {
            try {
                db_connection.close();
                db_connection = null;
            } catch (SQLException e) {
                getLogger().log(LogLevel.ERROR, e.getMessage(), e.fillInStackTrace());
            }
        }
    }
}

【问题讨论】:

  • (确保正确使用事务。否则发布的代码“不会很好”,即使[重新]使用准备好的语句。也就是说,INSERT/UPDATE 是“快速”的。COMMIT 是“慢”。)

标签: java sql jdbc prepared-statement


【解决方案1】:

您可以考虑使用addBatch() 并一次性执行后面的语句。另外,正如@pst 在您的问题中评论的那样,请考虑使用trasaction

你会做的方式如下:

boolean autoCommit = connection.getAutoCommit();
try{
    connection.setAutoCommit(false //Turn off autoCommit
    pstatement = db_connection.prepareStatement(PDSLnPConstants.UPSERT_SQL);

    int batchLimit = 1000;

    try{
        for (Entry<Integer, LinkedHashMap<Integer, String>> entry : MAPPING.entrySet()) {
            pstatement.setInt(1, entry.getKey());
            pstatement.setString(2, entry.getValue().get(LnPConstants.CGUID_ID));
            pstatement.setString(3, entry.getValue().get(LnPConstants.PGUID_ID));
            pstatement.setString(4, entry.getValue().get(LnPConstants.SGUID_ID));
            pstatement.setString(5, entry.getValue().get(LnPConstants.UID_ID));
            pstatement.setString(6, entry.getValue().get(LnPConstants.ULOC_ID));
            pstatement.setString(7, entry.getValue().get(LnPConstants.SLOC_ID));
            pstatement.setString(8, entry.getValue().get(LnPConstants.PLOC_ID));
            pstatement.setString(9, entry.getValue().get(LnPConstants.ALOC_ID));
            pstatement.setString(10, entry.getValue().get(LnPConstants.SITE_ID));

            pstatement.addBatch();
            batchLimit--;

            if(batchLimit == 0){
                pstatement.executeBatch();
                pstatement.clearBatch
                batchLimit = 1000;
            }
             pstatement.clearParameters();
        }
    }finally{
        //for the remaining ones
        pstatement.executeBatch();

        //commit your updates
        connection.commit();
    }
}finally{
    connection.setAutoCommit(autoCommit);
}

这个想法是为批量更新设置一个限制,并仅在达到特定限制时执行数据库更新。这样,您将数据库调用限制为您定义的每个 batchLimit 一次。这样会更快。

还要注意transaction,我刚刚展示了如何以及何时使用commit。这可能并不总是指向commit 的正确点,因为此决定将基于您的要求。如果出现异常,您可能还想执行rollback。因此,由您决定。

查看"Using Transaction" 教程以更好地了解如何使用transaction

【讨论】:

  • 您能否提供一个基于我的代码的示例,以便我清楚地了解如何在这里使用addBatch()?会有很大帮助的。
  • @Nevzz03:用示例代码更新了我的答案。希望这会有所帮助:)
  • 感谢 Sujay 的详细回答。感谢您的帮助。我刚刚按照您在进行更改后的建议,使用我目前正在使用的完整代码更新了我的问题。你能看一下,让我知道你是否觉得一切都很好,我没有做错什么吗?
  • @Nevzz03:如果您在尝试执行数据库更新时遇到SQLException,您可以考虑使用rollback。此外,我已将batchLimit 设置为 1000,您显然可以根据自己的要求进行调整。除此之外,我认为您的代码对我来说已经足够好了 :)
  • 哦,您忘记将 autoCommit 重置为其初始值。也要注意这一点
【解决方案2】:

是的,进行批量更新会显着提高您的性能。只需谷歌,我的首选答案是this one from Mkyong.com。否则,您的代码看起来不错。 “clearParameters()”并不是真正需要的,它甚至可能会消耗一些处理器周期。重要提示:如果启用了 AutoCommit,请不要忘记之前禁用它,并在更新后启用它,这再次带来了巨大的改进。

PS

以上建议也是基于我的经验。我刚刚注意到这个问题已经被问到here at Stackoverflow 并且答案非常详细。有关 PreparedStatements 和批处理的更多信息,请参见 Oracle 文档 here 和有关事务 (AutoCommit) here

【讨论】:

    【解决方案3】:

    我觉得你的代码不错。

    为了代码整洁,我将entry.getValue() 放入一个变量中(称为value)。
    并且无需拨打clearParameters()

    最后,当您不再需要准备好的语句时,请记住正确处理它 (close())。

    【讨论】:

    • 感谢 gd1 的建议。为什么你认为没有必要打电话给clearParameters()?我只是想从知识的角度了解一下。
    猜你喜欢
    • 2019-02-07
    • 1970-01-01
    • 2011-07-24
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-04
    相关资源
    最近更新 更多