2010-12-04 30 views
61

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

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

    myStatement.executeUpdate(); 
} 

me gustaría para optimizar esta opción para utilizar la sintaxis soportada-MySQL:

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

pero con un PreparedStatement Yo no sé de ninguna manera para hacer esto ya que no sé de antemano cuántos elementos contendrá array. Si no es posible con un PreparedStatement, ¿de qué otro modo puedo hacerlo (y aún así escapar de los valores del conjunto)?

Respuesta

123

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

Aquí está un ejemplo patada de salida:

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 artículos debido a que algunos controladores JDBC y/o DBS puede tener una limitación de la duración de lote.

Ver también:

+0

Perfecto, gracias! –

+21

Tus inserciones irán más rápido si las pones en transacciones ... es decir, envuelves con 'connection.setAutoCommit (false);' y 'connection.commit();' http://download.oracle.com/javase/tutorial/ jdbc/basics/transactions.html –

+0

Gracias por el consejo, Joshua! –

4

si se puede crear la instrucción SQL dinámica que puede hacer siguiente solución:

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(); 
+0

¡Creo que la respuesta aceptada es mucho mejor! ¡No sabía acerca de las actualizaciones por lotes y cuando comencé a escribir esta respuesta, esa respuesta aún no se envió! :) –

+0

Este enfoque es mucho más rápido que el aceptado. Lo pruebo, pero no encuentro por qué. @JohnS ¿sabes por qué? – julian0zzx

+0

@ julian0zzx no, pero quizás porque se ejecuta como sql único en lugar de múltiple. pero no estoy seguro. –

0

que puede haber enviar m Ultiple actualizaciones juntas en JDBC para enviar actualizaciones por lotes.

podemos utilizar Norma, objetos PreparedStatement y CallableStatement para la actualización bacth con el compromiso automático desactivar

addBatch() y executeBatch() funciones están disponibles con todos los objetos de los estados a tener BatchUpdate

aquí El método addBatch() agrega un conjunto de sentencias o parámetros al lote actual.

17

Cuando se utiliza MySQL conductor tiene que configurar la conexión parámetro rewriteBatchedStatements true (jdbc: mysql: // localhost: 3306/TestDB rewriteBatchedStatements = true).

Con este parámetro la instrucción se reescribe en la inserción masiva cuando la tabla se bloquea solo una vez y los índices se actualizan solo una vez. Entonces es mucho más rápido.

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

+0

este es un comentario para la ejecución de la construcción: statement.addBatch(); if ((i + 1)% 1000 == 0) { statement.executeBatch(); // Ejecuta cada 1000 elementos. } – MichalSv

+0

Al parecer, el controlador MySQL tiene un error http://bugs.mysql.com/bug.php?id=71528 Esto también causa problemas para los marcos ORM como Hibernate https://hibernate.atlassian.net/browse/HHH-9134 – Shailendra

+0

Sí. Esto es correcto por ahora también. Al menos para la versión de conector mysql '5.1.45'. –

2

En caso de que haya incrementado automáticamente en la tabla y necesite acceder a ella ... puede usar el siguiente enfoque ... Haga una prueba antes de usar porque getGeneratedKeys() en Statement porque depende del controlador utilizado. El código siguiente 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 estaba degradando 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(); 
    } 
} 
2

@Ali Shakiba su 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(); 
+0

Este es un enfoque mucho más rápido y mejor en toda la respuesta. Esta debería ser la respuesta aceptada –

Cuestiones relacionadas