Java: Inserir várias linhas no MySQL com PreparedStatement

Eu quero inserir várias linhas em uma tabela MySQL ao mesmo tempo usando Java. O número de linhas é dynamic. No passado eu estava fazendo …

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

Eu gostaria de otimizar isso para usar a syntax suportada pelo MySQL:

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

mas com um PreparedStatement eu não conheço nenhuma maneira de fazer isso, pois não sei de antemão quantos elementos o array conterá. Se não for possível com um PreparedStatement , como mais posso fazer isso (e ainda assim escaping dos valores da matriz)?

Você pode criar um lote por PreparedStatement#addBatch() e executá-lo por PreparedStatement#executeBatch() .

Aqui está um exemplo de kickoff:

 public void save(List 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. } } } } 

Ele é executado a cada 1000 itens porque alguns drivers JDBC e / ou DBs podem ter uma limitação no tamanho do lote.

Veja também :

  • Tutorial do JDBC – Usando o PreparedStatement
  • Tutorial JDBC – Usando Objetos de Instrução para Atualizações em Lote

Quando o driver MySQL é usado, você precisa definir o parâmetro de conexão rewriteBatchedStatements como true (jdbc: mysql: // localhost: 3306 / TestDB? RewriteBatchedStatements = true ).

Com esse parâmetro, a instrução é reescrita para inserir em massa quando a tabela é bloqueada apenas uma vez e os índices são atualizados apenas uma vez. Então é muito mais rápido.

Sem esse parâmetro, apenas a vantagem é um código-fonte mais limpo.

Se você pode criar sua instrução sql dinamicamente, você pode fazer a seguinte solução alternativa:

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

Caso você tenha auto incremento na tabela e precise acessá-lo .. você pode utilizar a seguinte abordagem … Faça teste antes de usar porque getGeneratedKeys () no Statement pois depende do driver utilizado. O código abaixo é testado no Maria DB 10.0.12 e no driver Maria JDBC 1.2

Lembre-se de que o aumento do tamanho do lote melhora o desempenho apenas até certo ponto … para minha configuração, aumentar o tamanho do lote acima de 500 estava realmente prejudicando o desempenho.

 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(); } } 

@Ali Shakiba seu código precisa de alguma modificação. Parte do erro:

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

Código atualizado:

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

Podemos enviar várias atualizações juntas no JDBC para enviar atualizações em lote.

podemos usar os objects Statement, PreparedStatement e CallableStatement para atualização da bacth com desabilitação do autocommit

As funções addBatch () e executeBatch () estão disponíveis com todos os objects de instrução para ter BatchUpdate

aqui o método addBatch () adiciona um conjunto de instruções ou parâmetros ao lote atual.

Intereting Posts