web-dev-qa-db-de.com

Java: Fügen Sie mit PreparedStatement mehrere Zeilen in MySQL ein

Ich möchte mit Java mehrere Zeilen gleichzeitig in eine MySQL-Tabelle einfügen. Die Anzahl der Zeilen ist dynamisch. In der Vergangenheit habe ich getan ...

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

    myStatement.executeUpdate();
}

Ich möchte dies optimieren, um die von MySQL unterstützte Syntax zu verwenden:

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

aber mit einem PreparedStatement kenne ich keine Möglichkeit, dies zu tun, da ich vorher nicht weiß, wie viele Elemente array enthalten werden. Wenn es mit einem PreparedStatement nicht möglich ist, wie kann ich es sonst tun (und trotzdem die Werte im Array umgehen)?

80
Tom Marthenal

Sie können eine Charge mit PreparedStatement#addBatch() erstellen und mit PreparedStatement#executeBatch() ausführen.

Hier ist ein Kickoff-Beispiel:

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.
            }
        }
    }
}

Es wird alle 1000 Elemente ausgeführt, da einige JDBC-Treiber und/oder DBs möglicherweise eine Beschränkung der Batch-Länge aufweisen.

Siehe auch :

158
BalusC

Wenn der MySQL-Treiber verwendet wird, müssen Sie den Verbindungsparameter rewriteBatchedStatements auf true ( jdbc:mysql://localhost:3306/TestDB?**rewriteBatchedStatements=true**) Setzen.

Mit diesem Parameter wird die Anweisung in Bulk Insert umgeschrieben, wenn die Tabelle nur einmal gesperrt und die Indizes nur einmal aktualisiert werden. Es geht also viel schneller.

Ohne diesen Parameter ist sauberer Quellcode der einzige Vorteil.

26
MichalSv

Wenn Sie Ihre SQL-Anweisung dynamisch erstellen können, können Sie Folgendes tun:

    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();
5
Ali Shakiba

Falls die Tabelle ein automatisches Inkrement enthält und Sie darauf zugreifen müssen, können Sie den folgenden Ansatz verwenden ... Führen Sie vor der Verwendung einen Test durch, da getGeneratedKeys () in Statement von dem verwendeten Treiber abhängt. Der folgende Code wurde mit Maria DB 10.0.12 und Maria JDBC-Treiber 1.2 getestet

Denken Sie daran, dass das Erhöhen der Stapelgröße die Leistung nur bis zu einem gewissen Grad verbessert. Bei meinem Setup hat das Erhöhen der Stapelgröße über 500 die Leistung tatsächlich beeinträchtigt.

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
gladiator

@ Ali Shakiba Ihr Code muss geändert werden. Fehlerteil:

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

Aktualisierter Code:

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();
3
vinay

in JDBC können mehrere Aktualisierungen gleichzeitig eingereicht werden, um Batch-Aktualisierungen zu übermitteln.

wir können Statement-, PreparedStatement- und CallableStatement-Objekte für Aktualisierungen mit deaktivierter automatischer Festschreibung verwenden

addBatch () und executeBatch () Funktionen sind für alle Anweisungsobjekte verfügbar, für die BatchUpdate erforderlich ist

hier fügt die Methode addBatch () dem aktuellen Stapel eine Reihe von Anweisungen oder Parametern hinzu.

0
kapil das