webentwicklung-frage-antwort-db.com.de

PreparedStatement mehrmals verwenden

kann ich im Fall der Verwendung von PreparedStatement mit einer einzelnen gemeinsamen Verbindung ohne Pool für jede dml/sql-Operation, die die Leistung von vorbereiteten Anweisungen beibehält, eine Instanz neu erstellen?

Ich meine:

for (int i=0; i<1000; i++) {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setObject(1, someValue);
    preparedStatement.executeQuery();
    preparedStatement.close();
}

anstatt:

PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i=0; i<1000; i++) {
    preparedStatement.clearParameters();
    preparedStatement.setObject(1, someValue);
    preparedStatement.executeQuery();
}
preparedStatement.close();

meine Frage ergibt sich aus der Tatsache, dass ich diesen Code in eine Multithread-Umgebung stellen möchte. Können Sie mir einen Rat geben? Vielen Dank

95
Steel Plume

Der zweite Weg ist ein bisschen effizienter, aber ein viel besserer Weg ist, sie in Stapeln auszuführen:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL);
    ) {
        for (Entity entity : entities) {
            statement.setObject(1, entity.getSomeProperty());
            // ...

            statement.addBatch();
        }

        statement.executeBatch();
    }
}

Sie sind jedoch abhängig von der Implementierung des JDBC-Treibers, wie viele Batches Sie gleichzeitig ausführen können. Sie können sie beispielsweise alle 1000 Chargen ausführen:

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

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

            statement.addBatch();
            i++;

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

In Multithread-Umgebungen müssen Sie sich darüber keine Gedanken machen, wenn Sie die Verbindung und die Anweisung im kürzestmöglichen Umfang innerhalb desselben Methodenblocks wie üblich abrufen und schließen JDBC-Idiom mit der Anweisung try-with-resources (siehe oben).

Wenn es sich um Transaktionsstapel handelt, möchten Sie die automatische Festschreibung der Verbindung deaktivieren und die Transaktion erst festschreiben, wenn alle Stapel abgeschlossen sind. Andernfalls kann es zu einer fehlerhaften Datenbank kommen, wenn der erste Stapel erfolgreich war und der spätere nicht.

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);

        try (PreparedStatement statement = connection.prepareStatement(SQL)) {
            // ...

            try {
                connection.commit();
            } catch (SQLException e) {
                connection.rollback();
                throw e;
            }
        }
    }
}
140
BalusC

Die Schleife in Ihrem Code ist nur ein stark vereinfachtes Beispiel, oder?

Es ist besser, das PreparedStatement nur einmal zu erstellen und es in der Schleife immer wieder zu verwenden.

In Situationen, in denen dies nicht möglich ist (weil es den Programmfluss zu sehr kompliziert hat), ist es immer noch von Vorteil, ein PreparedStatement zu verwenden, auch wenn Sie es nur einmal verwenden, da die serverseitige Arbeit (das Parsen der SQL und das Zwischenspeichern der Ausführungsplan) wird noch gekürzt.

Um die Situation zu lösen, in der Sie das Java-seitige PreparedStatement wiederverwenden möchten, verfügen einige JDBC-Treiber (z. B. Oracle) über eine Caching-Funktion: Wenn Sie ein PreparedStatement für dasselbe SQL auf derselben Verbindung erstellen, erhalten Sie dasselbe (zwischengespeicherte) Instanz.

Über Multithreading: Ich glaube nicht, dass JDBC-Verbindungen von mehreren Threads gemeinsam genutzt werden können (d. H. Von mehreren Threads gleichzeitig verwendet werden). Jeder Thread sollte seine eigene Verbindung aus dem Pool abrufen, sie verwenden und wieder in den Pool zurückgeben.

13
Thilo