web-dev-qa-db-fra.com

Java: insérez plusieurs lignes dans MySQL avec PreparedStatement

Je veux insérer plusieurs lignes dans une table MySQL à la fois en utilisant Java. Le nombre de lignes est dynamique. Dans le passé je faisais ...

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

    myStatement.executeUpdate();
}

Je voudrais optimiser ceci pour utiliser la syntaxe supportée par MySQL:

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

mais avec un PreparedStatement je ne connais aucun moyen de le faire car je ne sais pas à l'avance combien d'éléments array contiendra. Si ce n'est pas possible avec un PreparedStatement, comment puis-je le faire (tout en échappant les valeurs du tableau)?

80
Tom Marthenal

Vous pouvez créer un lot par PreparedStatement#addBatch() et l'exécuter par PreparedStatement#executeBatch() .

Voici un exemple de coup d'envoi:

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

Il est exécuté tous les 1 000 éléments car certains pilotes JDBC et/ou bases de données peuvent avoir une limite de longueur de lot.

Voir aussi :

158
BalusC

Lorsque le pilote MySQL est utilisé, vous devez définir le paramètre de connexion rewriteBatchedStatements sur true ( jdbc:mysql://localhost:3306/TestDB?**rewriteBatchedStatements=true**).

Avec ce paramètre, l'instruction est réécrite pour une insertion en bloc lorsque la table est verrouillée une seule fois et que les index ne sont mis à jour qu'une seule fois. Donc c'est beaucoup plus rapide.

Sans ce paramètre, seul le code source est plus propre.

26
MichalSv

Si vous pouvez créer votre déclaration SQL de manière dynamique, vous pouvez effectuer la solution suivante:

    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

Si vous avez une incrémentation automatique dans la table et que vous devez y accéder, vous pouvez utiliser l'approche suivante ... Effectuez un test avant d'utiliser car getGeneratedKeys () dans Statement car il dépend du pilote utilisé. Le code ci-dessous est testé sur Maria DB 10.0.12 et Maria JDBC driver 1.2.

N'oubliez pas que l'augmentation de la taille du lot n'améliore les performances que dans une certaine mesure ... pour mon installation, une taille de lot supérieure à 500 dégradait réellement les performances.

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, votre code a besoin d'être modifié. Partie d'erreur:

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

Code mis à jour:

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

nous pouvons soumettre simultanément plusieurs mises à jour dans JDBC pour soumettre des mises à jour par lots.

nous pouvons utiliser les objets Statement, PreparedStatement et CallableStatement pour la mise à jour de bacth avec disable autocommit

addBatch () et executeBatch () les fonctions sont disponibles avec tous les objets d'instruction pour que BatchUpdate soit disponible

ici, la méthode addBatch () ajoute un ensemble d'instructions ou de paramètres au lot actuel.

0
kapil das