Processamento em lote no JDBC e HIBERNATE

Olá!


Neste artigo, falarei brevemente sobre o processamento em lote de instruções SQL (DML): INSERT, UPDATE, DELETE, como uma das maneiras de obter maior desempenho.




Benefícios


Diferentemente da execução sequencial de cada consulta SQL, o processamento em lote possibilita o envio de um conjunto inteiro de consultas (pacote) em uma chamada, reduzindo o número de conexões de rede necessárias e permitindo que o banco de dados execute um certo número de consultas em paralelo, o que pode aumentar significativamente a velocidade de execução. Imediatamente, faça uma reserva para observar um efeito notável ao inserir, atualizar ou excluir grandes quantidades de dados em uma tabela do banco de dados.



Tabela de banco de dados


Como exemplo, usaremos a tabela de livros com os campos id e title.


Eu iriatítulo
10001API de persistência Java e Hibernate
10002Novo grande livro CSS
10003Primavera 5 para profissionais
10004PROGRAMAÇÃO EFICAZ Java

1. JDBC - processamento em lote


Antes de passar para exemplos de implementação, é necessário destacar vários pontos importantes:


  1. JDBC , supportsBatchUpdates() DatabaseMetaData, , . true, .
  2. , setAutoCommit(false). , commit() rollback(). rollback() SQL .
  3. — , JDBC Statement, PreparedStatement CallableStatement, .

, Statement, PreparedStatement CallableStatement . , BATCH_SIZE. , . , , Hibernate 10 50.


, SQL INSERT. UPDATE, DELETE .



1.1. Statement


Statement book.


connection.setAutoCommit(false);
try (Statement stmt = connection.createStatement()) { //(1)
    for (int i = 1; i <= SIZE; i++) {
        stmt.addBatch("INSERT INTO book (title) VALUES ('" + "JDBC Insert Example: " + i + "')"); //(2)
        if (i % BATCH_SIZE == 0 || i == SIZE) {
            try {
                int[] result = stmt.executeBatch(); //(3)
                connection.commit();
            } catch (BatchUpdateException ex) {
                Log(ex);
                connection.rollback();
            }
        }
    }
}

:

  1. C Statement;
  2. C void addBatch( String SQL );
  3. executeBatch(). executeBatch() .


Statement SQL INSERT, UPDATE, DELETE.



SQL , .



1.2. PreparedStatement


PreparedStatement book.


connection.setAutoCommit(false);
try (PreparedStatement pstmt = connection.prepareStatement("INSERT INTO book (title) VALUES (?)")) { //(1)
    for (int i = 1; i <= SIZE; i++) {
        pstmt.setString(1, "JDBC Insert Example: " + i); //(2)
        pstmt.addBatch(); //(3)
        if (i % BATCH_SIZE == 0 || i == SIZE) {
            try {
                int[] result = pstmt.executeBatch(); //(4)
                connection.commit();
            } catch (BatchUpdateException ex) {
                Log(ex);
                connection.rollback();
            }
        }
    }
}

:

  1. C PreparedStatement SQL ;
  2. , ;
  3. void addBatch();
  4. executeBatch().

3) 4) , Statement, — addBatch() .




SQL , , . , .



PreparedStatement SQL (INSERT, UPDATE, DELETE) Statement, - .



1.3. CallableStatement


CallableStatement .


, OUT INOUT.


CallableStatement book.


connection.setAutoCommit(false);
try (CallableStatement cstmt = connection.prepareCall("call insert_book(?)")) { //(1)
    for (int i = 1; i <= SIZE; i++) {
        cstmt.setString(1, "JDBC Insert Example: " + i); //(2)
        cstmt.addBatch(); //(3)
        if (i % BATCH_SIZE == 0 || i == SIZE) {
            try {
                int[] result = cstmt.executeBatch(); //(4)
                connection.commit();
            } catch (BatchUpdateException ex) {
                Log(ex);
                connection.rollback();
            }
        }
    }
}

:

  1. C CallableStatement ;
  2. , ;
  3. void addBatch();
  4. executeBatch().

, PreparedStatement.



, , .


- , .



CallableStatement , - .


1.4. BatchUpdateException


BatchUpdateException, . BatchUpdateException , SQL , - , ResultSet. BatchUpdateException ( getUpdateCounts()), , executeBatch. , SQL . , c (Statement.EXECUTE_FAILED) .


:


...
} catch (BatchUpdateException ex) {
    int[] updateCount = ex.getUpdateCounts();
    int count = 1;
    for (int i : updateCount) {
        if (i == Statement.EXECUTE_FAILED) {
            System.out.println("Request " + count + ": Execute failed");
        } else {
            System.out.println("Request " + count + ": OK");
        }
        count++;
    }
}
...

. , , , , . BatchUpdateException SQL , . .


2. Hibernate —


2.1.


, , hibernate.jdbc.batch_size Hibernate.cfg.xml . Hibernate SQL INSERT, UPDATE, DELETE JDBC . SQL JDBC hibernate.order_inserts, hibernate.order_updates true, Hibernate SQL . SQL , JDBC Hibernate addBatch() PreparedStatement, SQL .


Hibernate.cfg.xml


...
<property name="hibernate.jdbc.batch_size">50</property>
<property name="hibernate.order_inserts">true</property>
<property name="hibernate.order_updates">true</property>
...

2.2.


. , . , Hibernate , OutOfMemoryException. 2 :


  1. flush() clear() .

    :


    try (Session session = HibernateUtil.getSessionFactory().getCurrentSession()) {
        Transaction transaction = session.getTransaction();
        transaction.begin();
        for (int i = 1; i <= SIZE; i++) {
            session.persist(new Book("Hibernate Insert Example: " + i));
            if (i % BATCH_SIZE == 0) {
                // Flush and clear the cache every batch
                session.flush();
                session.clear();
            }
        }
        transaction.commit();
    }
    

  2. StatelessSession. StatelessSession . , , , (aliasing) . , Hibernate. Hibernate.

    :


    try (StatelessSession session = HibernateUtil.getSessionFactory().openStatelessSession()) {
        Transaction transaction = session.getTransaction();
        transaction.begin();
        for (int i = 1; i <= SIZE; i++) {
            session.persist(new Book("Hibernate Insert Example: " + i));
        }
        transaction.commit();
    }
    



A execução em lote de consultas SQL é uma das maneiras conhecidas de melhorar o desempenho em que você deve prestar atenção. Reduzir o número de conexões de rede ao banco de dados e aumentar a velocidade de execução da consulta é uma vantagem significativa a favor do uso do processamento em lote.


Exemplos de código podem ser encontrados no GitHub .


All Articles