简体   繁体   English

有效地多次使用 Prepared Statement

[英]Using Prepared Statement multiple times efficiently

Below is the code which I am using to insert multiple records( around 5000-7000) in the Oracle Database using Prepared Statement.下面是我使用 Prepared Statement 在 Oracle 数据库中插入multiple records( around 5000-7000)的代码。

The way I am doing currently is good?我目前的做法好吗? Or it can be improve more using some batch thing ?或者它可以使用一些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();
}

Udpated Code That I am Using:-我正在使用的更新代码:-

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());
            }
        }
    }
}

You can think of using addBatch() and executing a back of statements in one shot.您可以考虑使用addBatch()并一次性执行后面的语句。 Also, as @pst commented in your question, consider using trasaction .另外,正如@pst 在您的问题中评论的那样,请考虑使用trasaction

The way you would do is as follows:你会做的方式如下:

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);
}

The idea is to set a limit for batch updates and execute a database update only when you reach a particular limit.这个想法是为批量更新设置一个限制,并且只有在达到特定限制时才执行数据库更新。 This way you're limiting a database call to once every batchLimit that you've defined.这样,您将数据库调用限制为您定义的每个batchLimit一次。 This way it would be faster.这样会更快。

Also note for the transaction , I've just shown how and when to commit .还要注意transaction ,我刚刚展示了如何以及何时commit This might not always be the correct point to commit because this decision would be based on your requirement.这可能并不总是正确的commit点,因为此决定将基于您的要求。 You might also want to perform a rollback in case of an exception.您可能还希望在发生异常时执行rollback So it's upto you to decide.因此,由您决定。

Have a look at "Using Transaction" tutorial to get a better picture of how to use transaction .查看“使用事务”教程,以更好地了解如何使用transaction

Your piece of code seems good to me.你的一段代码对我来说似乎很好。

Just for code cleanness, I'd put entry.getValue() into a variable (call it value ).只是为了代码清洁,我将entry.getValue()放入一个变量中(称之为value )。
And there's no need to call clearParameters() .并且没有必要调用clearParameters()

Last, remember to correctly dispose the prepared statement when you don't need it anymore ( close() ).最后,记住在不再需要准备好的语句时正确处理它( close() )。

Yes, doing batch updates would significantly improve your performance.是的,进行批量更新会显着提高您的性能。 Just google for it, my preferred answer is this one from Mkyong.com .只需谷歌,我的首选答案是来自 Mkyong.com 的答案。 Else, your code looks ok.否则,您的代码看起来不错。 "clearParameters()" is not really necessary, it might even consume some processor cycles. “clearParameters()”并不是真正需要的,它甚至可能会消耗一些处理器周期。 Important: if AutoCommit was enabled, don't forget to disable it before, and enable it after doing the updates, this brings again a tremendous improvement.重要提示:如果启用了 AutoCommit,请不要忘记之前禁用它,并在更新后启用它,这再次带来了巨大的改进。

PS附言

Above recommendation is based also on my experience.以上建议也是基于我的经验。 I've just noted that this question was already asked here at Stackoverflow and the answer is very detailed.我刚刚注意到这个问题已经在 Stackoverflow上提出过,答案非常详细。 More on PreparedStatements and batches can be found in the Oracle docs here and about Transactions (AutoCommit) here .有关 PreparedStatements 和批次的更多信息,请参见此处的 Oracle 文档和此处的事务(自动提交)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM