Procesamiento por lotes en JDBC y HIBERNATE

¡Hola!


En este artículo, hablaré brevemente sobre el procesamiento por lotes de las declaraciones SQL (DML): INSERT, UPDATE, DELETE, como una de las formas de lograr un mayor rendimiento.




Beneficios


A diferencia de la ejecución secuencial de cada consulta SQL, el procesamiento por lotes permite enviar un conjunto completo de consultas (paquete) en una llamada, lo que reduce el número de conexiones de red requeridas y permite que la base de datos ejecute un cierto número de consultas en paralelo, lo que puede aumentar significativamente la velocidad de ejecución. Inmediatamente haga una reserva de que se puede ver un efecto notable al insertar, actualizar o eliminar grandes cantidades de datos en una tabla de base de datos.



Tabla DB


Como ejemplo, utilizaremos la tabla de libros con los campos id y title.


carné de identidadtítulo
10001Java Persistence API e Hibernate
10002Nuevo libro grande de CSS
10003Spring 5 para profesionales
10004PROGRAMACIÓN EFICAZ Java

1. JDBC - procesamiento por lotes


Antes de pasar a los ejemplos de implementación, es necesario resaltar varios puntos 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();
    }
    



La ejecución por lotes de consultas SQL es una de las formas conocidas de mejorar el rendimiento a las que debe prestar atención. Reducir el número de conexiones de red a la base de datos y aumentar la velocidad de ejecución de consultas es una ventaja significativa a favor del uso del procesamiento por lotes.


Se pueden encontrar ejemplos de código en GitHub .


All Articles