La mayoría de los controladores JDBC proporcionan mejoras de rendimiento al agrupar varias llamadas a la misma declaración compilada. Al agrupar las actualizaciones en lotes, limita el número de viajes de ida y vuelta a la base de datos.

Operaciones por lotes básicas utilizando JdbcTemplate

Procesa por lotes JdbcTemplate implementando dos métodos de la interfaz especial BatchPreparedStatementSetter y pasando esta implementación como segundo parámetro en la llamada al método batchUpdate. El método getBatchSize se puede utilizar para establecer los valores de los parámetros de la declaración compilada. El método setValues se puede utilizar para establecer los valores de los parámetros de una declaración preparada. Este método se llama el número de veces especificado en la llamada getBatchSize. El siguiente ejemplo actualiza la tabla t_actor en función de las entradas de una lista, utilizando la lista completa como lote:

Java
clase pública JdbcActorDao implementa ActorDao { private JdbcTemplate jdbcTemplate; setDataSource público vacío (Fuente de datos fuente de datos) { this.jdbcTemplate = nuevo JdbcTemplate (fuente de datos); } public int[] batchUpdate(lista final<Actor> actores) { return this.jdbcTemplate.batchUpdate( "actualizar t_actor set first_name =?, last_name =? donde id =?", new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps , int i) lanza SQLException { Actor actor = actores.get(i); ps.setString(1, actor.getFirstName()); ps.setString(2, actor.getLastName()); ps.setLong(3, actor .getId().longValue()); } public int getBatchSize() { return actores.size(); } }); } // ... métodos adicionales }
Kotlin
clase JdbcActorDao(dataSource: DataSource) : ActorDao { valor privado jdbcTemplate = JdbcTemplate(dataSource) fun batchUpdate(actores: Lista<Actor>): IntArray { return jdbcTemplate.batchUpdate( "actualizar t_actor set first_name =?, last_name =? donde id =?", objeto: BatchPreparedStatementSetter { anular fun setValues (ps: PreparedStatement, i: Int) { ps.setString(1, actores[i].firstName) ps.setString(2, actores[i].lastName) ps.setLong(3, actores[i].id) } anular divertido getBatchSize() = actores.size }) } // ... métodos adicionales }

Si está manejando una secuencia de actualización o leyendo desde un archivo, es posible que ya tenga un tamaño de lote preferido, pero es posible que el último lote no tenga tantos registros. En este caso, puede utilizar la interfaz InterruptibleBatchPreparedStatementSetter, que le permite interrumpir el lote después de que se agote la fuente de datos de entrada. El método isBatchExhausted le permite señalar el final de un lote.

Operaciones por lotes utilizando una lista de objetos

Plantilla JdbcTemplate y NamedParameterJdbcTemplate proporcionan una forma alternativa de proporcionar actualizaciones por lotes. En lugar de implementar una interfaz por lotes especializada, proporciona todos los valores de los parámetros en la llamada como una lista. El marco itera a través de estos valores y utiliza el definidor interno de las declaraciones compiladas. La API difiere dependiendo de si utiliza parámetros con nombre. Para los parámetros con nombre, especifica una matriz de SqlParameterSource, una entrada para cada miembro del grupo. Puede utilizar los métodos auxiliares SqlParameterSourceUtils.createBatch para crear esta matriz pasando una matriz de objetos basados en beans (usando captadores correspondientes a los parámetros), instancias Map con Cadena-key (que contiene los parámetros correspondientes como valores), o una combinación de ambos.

El siguiente ejemplo muestra una actualización por lotes utilizando parámetros con nombre:

Java
        clase pública JdbcActorDao implementa ActorDao { private NamedParameterTemplate
            nombradoParameterJdbcTemplate; setDataSource público vacío (Fuente de datos fuente de datos) {
            this.namedParameterJdbcTemplate = nuevo NamedParameterJdbcTemplate (fuente de datos); } public int[]
            batchUpdate(List<Actor> actores) { return this.namedParameterJdbcTemplate.batchUpdate( "actualizar
            t_actor set first_name = :firstName, last_name = :lastName donde id = :id",
            SqlParameterSourceUtils.createBatch(actores)); } // ... métodos adicionales }
Kotlin
clase JdbcActorDao(dataSource: DataSource) : ActorDao { valor privado nameParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource) fun batchUpdate(actores: Lista<Actor>): IntArray { return this.namedParameterJdbcTemplate.batchUpdate( "actualizar t_actor set first_name = :firstName, last_name = :lastName donde id = :id", SqlParameterSour ceUtils .createBatch(actores)); } // ... métodos adicionales }

Para una declaración SQL que utiliza marcadores de posición clásicos ?, se pasa una lista que contiene un matriz de objetos con valores de actualización. Esta matriz de objetos debe contener un elemento para cada marcador de posición en la declaración SQL y deben estar en el mismo orden en que se definen en la declaración SQL.

El siguiente ejemplo es similar al anterior. , excepto que utiliza marcadores de posición JDBC clásicos ?:

Java
clase pública JdbcActorDao implementa ActorDao { private JdbcTemplate jdbcTemplate; setDataSource público vacío (Fuente de datos fuente de datos) { this.jdbcTemplate = nuevo JdbcTemplate (fuente de datos); } public int[] batchUpdate(lista final<Actor> actores) { Lista<Objeto[]> lote = nueva ArrayList<Object[]>(); for (Actor actor : actores) { Objeto[] valores = nuevo Objeto[] { actor.getFirstName(), actor.getLastName(), actor.getId()}; lote.add(valores); } return this.jdbcTemplate.batchUpdate( "actualizar t_actor set first_name =?, last_name =? donde id =?, lote); } // ... métodos adicionales }
Kotlin
clase JdbcActorDao(dataSource: DataSource) : ActorDao { private val jdbcTemplate = JdbcTemplate(dataSource) fun batchUpdate(actores: Lista<Actor>): IntArray { val lote = mutableListOf<Array<Any>>() for (actor en actores) { lote.add(arrayOf(actor.firstName , actor.lastName, actor.id)) } return jdbcTemplate.batchUpdate( "update t_actor set first_name =?, last_name =? donde id =?, lote) } // ... métodos adicionales }

Todos los métodos de actualización por lotes que describimos anteriormente devuelven una matriz int que contiene el número de filas afectadas para cada entrada del lote. Este contador lo informa el controlador JDBC. Si el contador no está disponible, el controlador JDBC devuelve el valor -2.

En tal escenario , al configurar automáticamente los valores subyacentes PreparedStatement, el tipo JDBC correspondiente para cada valor debe derivarse del tipo Java dado. Si bien esto generalmente funciona bien, existe la posibilidad de que surjan problemas (por ejemplo, con los valores null contenidos en el mapa). En tal caso, Spring llama a ParameterMetaData.getParameterType de forma predeterminada, lo que puede resultar costoso cuando se utiliza el controlador JDBC. Debería utilizar el controlador más reciente y considerar establecer la propiedad spring.jdbc.getParameterType.ignore en true (como una propiedad del sistema JVM o mediante el SpringProperties ) si está experimentando un problema de rendimiento (como se informa en Oracle 12c, JBoss y PostgreSQL).

Además, se podría considerar especificar explícitamente los tipos JDBC apropiados a través de un BatchPreparedStatementSetter (como se mostró anteriormente) o mediante una matriz explícita de tipos pasados a la llamada basada en List<Object[]>. ;, o mediante una llamada a registerSqlType en una instancia personalizada de MapSqlParameterSource, o mediante BeanPropertySqlParameterSource, que infiere el tipo de SQL a partir de el tipo de propiedad declarado en Java, incluso para un valor vacío.

Operaciones por lotes usando múltiples paquetes

El ejemplo anterior de actualización por lotes trató con paquetes que son tan grandes que necesitas dividirlos en varios paquetes más pequeños. Puede hacer esto usando los métodos mencionados anteriormente haciendo varias llamadas al método batchUpdate, pero ahora hay un método más conveniente. Este método acepta, además de la declaración SQL, una Collection de objetos que contienen parámetros, el número de actualizaciones para cada paquete y un ParameterizedPreparedStatementSetter para establecer los valores del parámetros de declaración compilados. El marco itera a través de los valores proporcionados y divide las llamadas de actualización en lotes del tamaño especificado.

El siguiente ejemplo muestra una actualización por lotes que utiliza un tamaño de lote de 100:

Java
 clase pública JdbcActorDao implementa ActorDao { JdbcTemplate privado jdbcTemplate; setDataSource público vacío (Fuente de datos fuente de datos) { this.jdbcTemplate = nuevo JdbcTemplate (fuente de datos); } public int[][] batchUpdate(colección final<Actor> actores) { int[][] updateCounts = jdbcTemplate.batchUpdate( "actualizar t_actor set first_name =?, last_name =? donde id =?, actores, 100, ( PreparedStatement ps, Actor actor) -> { ps.setString(1, actor.getFirstName()); ps.setString(2, actor.getLastName()); ps.setLong(3, actor.getId().longValue( ) ); }); devolver recuentos de actualizaciones; } // ... métodos adicionales }
Kotlin
clase JdbcActorDao(dataSource: DataSource) : ActorDao { private val jdbcTemplate = JdbcTemplate(dataSource) fun batchUpdate(actores: Lista<Actor>): Array<IntArray> { return jdbcTemplate.batchUpdate( "actualizar t_actor set first_name =?, last_name =? donde id =?", actores, 100) { ps, argumento -> ps.setString(1, argumento.primerNombre) ps.setString(2, argumento.apellido) ps.setLong(3, argumento.id) } } // ... métodos adicionales }

El método de actualización por lotes para esta llamada devuelve una matriz de matrices int que contiene una entrada de matriz para cada lote con una matriz del número de filas afectadas por cada actualización. La longitud de la matriz de nivel superior indica la cantidad de paquetes en ejecución y la longitud de la matriz de segundo nivel indica la cantidad de actualizaciones en ese paquete. La cantidad de actualizaciones en cada paquete debe coincidir con el tamaño de paquete especificado para todos los paquetes (excepto el último, que puede ser más pequeño), dependiendo de la cantidad total de objetos actualizables especificados. El contador de actualizaciones para cada declaración de actualización es el contador informado por el controlador JDBC. Si el contador no está disponible, el controlador JDBC devuelve -2.