Java: Inserte varias filas en MySQL con PreparedStatement


Quiero insertar varias filas en una tabla MySQL a la vez usando Java. El número de filas es dinámico. En el pasado estaba haciendo...

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

    myStatement.executeUpdate();
}

Me gustaría optimizar esto para usar la sintaxis soportada por MySQL:

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

Pero con un PreparedStatement no conozco ninguna manera de hacer esto ya que no sé de antemano cuántos elementos array contendrán. Si no es posible con un PreparedStatement, ¿de qué otra manera puedo hacerlo (y aún así escapar de los valores en la matriz)?

Author: Jonas, 2010-12-04

6 answers

Puede crear un lote por PreparedStatement#addBatch() y ejecutarlo por PreparedStatement#executeBatch().

Aquí hay un ejemplo de lanzamiento:

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

Se ejecuta cada 1000 elementos porque algunos controladores JDBC y/o DBs pueden tener una limitación en la longitud del lote.

Véase también :

 146
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-09 20:00:31

Cuando se utiliza el controlador MySQL, debe establecer el parámetro de conexión rewriteBatchedStatements en true ( jdbc:mysql://localhost:3306/TestDB?**rewriteBatchedStatements=true**).

Con este parámetro, la instrucción se reescribe a bulk insert cuando la tabla se bloquea solo una vez y los índices se actualizan solo una vez. Así que es mucho más rápido.

Sin este parámetro, la única ventaja es un código fuente más limpio.

 21
Author: MichalSv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-18 12:44:24

Si puede crear su sentencia sql dinámicamente, puede hacer lo siguiente:

    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
Author: Ali Shakiba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-04 19:01:16

En caso de que tenga incremento automático en la tabla y necesite acceder a ella.. puede utilizar el siguiente enfoque... Haga la prueba antes de usar porque getGeneratedKeys () en la instrucción porque depende del controlador utilizado. El siguiente código se prueba en Maria DB 10.0.12 y Maria JDBC driver 1.2

Recuerde que aumentar el tamaño del lote mejora el rendimiento solo hasta cierto punto... para mi configuración, aumentar el tamaño del lote por encima de 500 en realidad degradaba el rendimiento.

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
Author: gladiator,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-30 07:29:41

@Ali Shakiba tu código necesita algunas modificaciones. Parte de error:

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

Código actualizado:

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
Author: vinay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-18 16:56:18

Podemos enviar varias actualizaciones juntas en JDBC para enviar actualizaciones por lotes.

Podemos usar los objetos Statement, PreparedStatement y CallableStatement para bacth update con disable autocommit

Las funciones addBatch() y executeBatch() están disponibles con todos los objetos de instrucción para tener batchUpdate

Aquí el método addBatch() añade un conjunto de instrucciones o parámetros al lote actual.

 0
Author: kapil das,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-15 14:18:40