Java: Myed में कई पंक्तियों को प्रिपेयरडस्टेट के साथ डालें


88

मैं जावा का उपयोग करते हुए एक बार में एक MySQL टेबल में कई पंक्तियों को सम्मिलित करना चाहता हूं। पंक्तियों की संख्या गतिशील है। अतीत में मैं कर रहा था ...

for (String element : array) {
    myStatement.setString(1, element[0]);
    myStatement.setString(2, element[1]);

    myStatement.executeUpdate();
}

मैं MySQL समर्थित सिंटैक्स का उपयोग करने के लिए इसे अनुकूलित करना चाहता हूं:

INSERT INTO table (col1, col2) VALUES ('val1', 'val2'), ('val1', 'val2')[, ...]

लेकिन PreparedStatementमैं ऐसा करने का कोई तरीका नहीं जानता क्योंकि मैं पहले से नहीं जानता कि कितने तत्व arrayशामिल होंगे। यदि यह संभव नहीं है PreparedStatement, तो मैं इसे कैसे कर सकता हूं (और अभी भी सरणी में मूल्यों से बच सकता हूं)?

जवाबों:


177

आप PreparedStatement#addBatch()इसके द्वारा एक बैच बना सकते हैं और उस पर अमल कर सकते हैं PreparedStatement#executeBatch()

यहां एक किकऑफ उदाहरण दिया गया है:

public void save(List<Entity> entities) throws SQLException {
    try (
        Connection connection = database.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_INSERT);
    ) {
        int i = 0;

        for (Entity entity : entities) {
            statement.setString(1, entity.getSomeProperty());
            // ...

            statement.addBatch();
            i++;

            if (i % 1000 == 0 || i == entities.size()) {
                statement.executeBatch(); // Execute every 1000 items.
            }
        }
    }
}

यह प्रत्येक 1000 वस्तुओं को निष्पादित किया जाता है क्योंकि कुछ JDBC ड्राइवर और / या DBs की बैच लंबाई पर सीमा हो सकती है।

यह भी देखें :


26
यदि आप उन्हें लेन-देन में लगाते हैं तो आपकी आवेषण तेजी से बढ़ेगी ... यानी लपेटें connection.setAutoCommit(false);और connection.commit(); download.oracle.com/javase/tutorial/jdbc/basics/…
जोशुआ मार्टेल

1
ऐसा लगता है कि यदि आप 999 आइटम हैं तो एक खाली बैच निष्पादित कर सकते हैं।
djechlin

2
@electricalbah इसे आम तौर पर निष्पादित करेगा क्योंकिi == entities.size()
योहन एआई

तैयार कथनों का उपयोग करते हुए बैच की नौकरियों को एक साथ रखने पर यहां एक और अच्छा संसाधन है। viralpatel.net/blogs/batch-insert-in-java-jdbc
डैनी

1
@ AndréPaulo: किसी भी SQL INSERT को तैयार स्टेटमेंट के लिए उपयुक्त है। बुनियादी उदाहरणों के लिए JDBC ट्यूटोरियल लिंक देखें। यह ठोस प्रश्न से संबंधित नहीं है।
BalusC

30

जब MySQL ड्राइवर का उपयोग किया जाता है तो आपको कनेक्शन परम rewriteBatchedStatementsको सही पर सेट करना होगा ( jdbc:mysql://localhost:3306/TestDB?**rewriteBatchedStatements=true**)

इस परम के साथ बयान को तब सम्मिलित किया जाता है जब तालिका केवल एक बार बंद की जाती है और अनुक्रमित केवल एक बार अपडेट किए जाते हैं। इसलिए यह ज्यादा तेज है।

इस परम के बिना केवल लाभ स्रोत कोड क्लीनर है।


यह निर्माण के लिए प्रदर्शन के लिए टिप्पणी है: विवरण .addBatch (); if ((i + 1)% 1000 == 0) {statement.executeBatch (); // प्रत्येक 1000 वस्तुओं को निष्पादित करें। }
मीकलसेव

जाहिरा तौर पर MySQL चालक एक बग है bugs.mysql.com/bug.php?id=71528 यह भी हाइबरनेट तरह ORM चौखटे में समस्या आ जाती hibernate.atlassian.net/browse/HHH-9134
शैलेंद्र

हाँ। यह अभी के लिए भी सही है। कम से कम 5.1.45mysql कनेक्टर संस्करण के लिए।
v.ladynev

<विरूपण साक्ष्य> mysql-कनेक्टर-जावा </ विरूपण साक्ष्य> <संस्करण> 8.0.14 </ संस्करण> बस जाँच की यह 8.0.14 का सही है। जोड़ने के बिना rewriteBatchedStatements=trueकोई प्रदर्शन लाभ नहीं है।
विन्सेंट मैथ्यू

7

यदि आप अपने sql स्टेटमेंट को गतिशील रूप से बना सकते हैं तो आप निम्नलिखित वर्कअराउंड कर सकते हैं:

String myArray[][] = { { "1-1", "1-2" }, { "2-1", "2-2" }, { "3-1", "3-2" } };

StringBuffer mySql = new StringBuffer("insert into MyTable (col1, col2) values (?, ?)");

for (int i = 0; i < myArray.length - 1; i++) {
    mySql.append(", (?, ?)");
}

myStatement = myConnection.prepareStatement(mySql.toString());

for (int i = 0; i < myArray.length; i++) {
    myStatement.setString(i, myArray[i][1]);
    myStatement.setString(i, myArray[i][2]);
}
myStatement.executeUpdate();

मेरा मानना ​​है कि स्वीकृत उत्तर कहीं बेहतर है !! मुझे बैच अद्यतनों के बारे में पता नहीं था और जब मुझे यह उत्तर लिखना शुरू किया गया था तो उत्तर अभी तक प्रस्तुत नहीं किया गया था !!! :)
अली शाकिबा

यह दृष्टिकोण स्वीकृत की तुलना में बहुत तेज है। मैं इसका परीक्षण करता हूं, लेकिन ऐसा क्यों नहीं पाता। @ जॉन्स क्या आप जानते हैं कि क्यों?
julian0zzx

@ julian0zzx नहीं, लेकिन शायद इसे कई के बजाय एकल sql के रूप में निष्पादित किया जाता है। लेकिन मुझे यकीन नहीं।
अली शाकिबा

3

यदि आपके पास तालिका में ऑटो वेतन वृद्धि है और इसे एक्सेस करने की आवश्यकता है .. तो आप निम्नलिखित दृष्टिकोण का उपयोग कर सकते हैं ... उपयोग करने से पहले परीक्षण करें क्योंकि GetGeneratedKeys () विवरण में क्योंकि यह ड्राइवर पर निर्भर करता है। नीचे दिए गए कोड का परीक्षण मारिया DB 10.0.12 और मारिया JDBC ड्राइवर 1.2 पर किया गया है

याद रखें कि बढ़ते हुए बैच का आकार केवल एक निश्चित सीमा तक ही प्रदर्शन में सुधार करता है ... मेरे सेटअप के लिए 500 से ऊपर बैच का आकार बढ़ाना वास्तव में प्रदर्शन को कम कर रहा था।

public Connection getConnection(boolean autoCommit) throws SQLException {
    Connection conn = dataSource.getConnection();
    conn.setAutoCommit(autoCommit);
    return conn;
}

private void testBatchInsert(int count, int maxBatchSize) {
    String querySql = "insert into batch_test(keyword) values(?)";
    try {
        Connection connection = getConnection(false);
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        boolean success = true;
        int[] executeResult = null;
        try {
            pstmt = connection.prepareStatement(querySql, Statement.RETURN_GENERATED_KEYS);
            for (int i = 0; i < count; i++) {
                pstmt.setString(1, UUID.randomUUID().toString());
                pstmt.addBatch();
                if ((i + 1) % maxBatchSize == 0 || (i + 1) == count) {
                    executeResult = pstmt.executeBatch();
                }
            }
            ResultSet ids = pstmt.getGeneratedKeys();
            for (int i = 0; i < executeResult.length; i++) {
                ids.next();
                if (executeResult[i] == 1) {
                    System.out.println("Execute Result: " + i + ", Update Count: " + executeResult[i] + ", id: "
                            + ids.getLong(1));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            success = false;
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (pstmt != null) {
                pstmt.close();
            }
            if (connection != null) {
                if (success) {
                    connection.commit();
                } else {
                    connection.rollback();
                }
                connection.close();
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

3

@ अली शकीबा आपके कोड को कुछ संशोधन की आवश्यकता है। त्रुटि भाग:

for (int i = 0; i < myArray.length; i++) {
     myStatement.setString(i, myArray[i][1]);
     myStatement.setString(i, myArray[i][2]);
}

अपडेटेड कोड:

String myArray[][] = {
    {"1-1", "1-2"},
    {"2-1", "2-2"},
    {"3-1", "3-2"}
};

StringBuffer mySql = new StringBuffer("insert into MyTable (col1, col2) values (?, ?)");

for (int i = 0; i < myArray.length - 1; i++) {
    mySql.append(", (?, ?)");
}

mysql.append(";"); //also add the terminator at the end of sql statement
myStatement = myConnection.prepareStatement(mySql.toString());

for (int i = 0; i < myArray.length; i++) {
    myStatement.setString((2 * i) + 1, myArray[i][1]);
    myStatement.setString((2 * i) + 2, myArray[i][2]);
}

myStatement.executeUpdate();

यह पूरे उत्तर में बहुत तेज और बेहतर दृष्टिकोण है। यह स्वीकृत उत्तर होना चाहिए
अरुण शंकर

1
जैसा कि स्वीकृत उत्तर में बताया गया है, कुछ JDBC ड्राइवर / डेटाबेस में उन पंक्तियों की संख्या पर सीमा होती है जिन्हें आप INSERT विवरण में शामिल कर सकते हैं। उपरोक्त उदाहरण के मामले में, अगर myArrayउस सीमा से अधिक लंबाई है, तो आप एक अपवाद को मार देंगे। मेरे मामले में, मेरे पास 1,000 पंक्ति की सीमा है जो एक बैच निष्पादन की आवश्यकता को पूरा करती है, क्योंकि मैं संभावित रूप से किसी भी रन पर 1,000 से अधिक पंक्तियों को अपडेट कर सकता हूं। इस प्रकार के कथन को सैद्धांतिक रूप से ठीक काम करना चाहिए यदि आप जानते हैं कि आप अधिकतम अनुमत से कम सम्मिलित कर रहे हैं। मन में कुछ रखने के लिए।
डैनी बुलिस

स्पष्ट करने के लिए, उपरोक्त उत्तर में बैच लंबाई पर JDBC ड्राइवर / डेटाबेस सीमाओं का उल्लेख है, लेकिन एक सम्मिलित विवरण में शामिल पंक्तियों की संख्या की सीमा भी हो सकती है, जैसा कि मैंने अपने मामले में देखा है।
डैनी बुलिस

0

हम बैच अपडेट सबमिट करने के लिए JDBC में एक साथ कई अपडेट सबमिट कर सकते हैं।

हम अक्षम ऑटोकॉमिट के साथ bacth अपडेट के लिए स्टेटमेंट, रेडीस्टेडमेंट और कॉल करने योग्य वस्तुओं का उपयोग कर सकते हैं

addBatch () और निष्पादित करें () फ़ंक्शन सभी स्टेटमेंट ऑब्जेक्ट्स के साथ बैचअपडेट के लिए उपलब्ध हैं

यहाँ addBatch () विधि वर्तमान बैच में बयानों या मापदंडों का एक सेट जोड़ती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.