Después de tanto trabajar ya hallamos la respuesta de esta contratiempo que muchos los lectores de nuestro sitio presentan. Si tienes algún detalle que aportar no dejes de compartir tu información.
Solución:
Para obtener una inserción masiva con Sring Boot y Spring Data JPA, solo necesita dos cosas:
-
establecer la opción
spring.jpa.properties.hibernate.jdbc.batch_size
al valor apropiado que necesita (por ejemplo: 20). -
utilizar
saveAll()
método de su repositorio con la lista de entidades preparadas para insertar.
El ejemplo de trabajo está aquí.
Con respecto a la transformación de la declaración de inserción en algo como esto:
INSERT INTO table VALUES (1, 2), (3, 4), (5, 6)
el tal está disponible en PostgreSQL: puede establecer la opción reWriteBatchedInserts
para true en conexión jdbc string:
jdbc:postgresql://localhost:5432/db?reWriteBatchedInserts=true
entonces el controlador jdbc hará esta transformación.
Puede encontrar información adicional sobre el procesamiento por lotes aquí.
ACTUALIZADO
Proyecto de demostración en Kotlin: sb-kotlin-batch-insert-demo
ACTUALIZADO
Hibernate deshabilita el procesamiento por lotes de inserción en el nivel de JDBC de forma transparente si usa un
IDENTITY
generador de identificadores.
El problema subyacente es el siguiente código en SimpleJpaRepository:
@Transactional
public S save(S entity)
if (entityInformation.isNew(entity))
em.persist(entity);
return entity;
else
return em.merge(entity);
Además de la configuración de la propiedad del tamaño del lote, debe asegurarse de que las llamadas a la clase SimpleJpaRepository persistan y no se fusionen. Hay algunos enfoques para resolver esto: use un @Id
generador que no consulta la secuencia, como
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
var id: Long
O forzar la persistencia para tratar los registros como nuevos al hacer que su entidad implemente Persistable y anule el isNew()
llamada
@Entity
class Thing implements Pesistable
var value: Int,
@Id
@GeneratedValue
var id: Long = -1
@Transient
private boolean isNew = true;
@PostPersist
@PostLoad
void markNotNew()
this.isNew = false;
@Override
boolean isNew()
return isNew;
O anular el save(List)
y use el administrador de la entidad para llamar persist()
@Repository
public class ThingRepository extends SimpleJpaRepository
private EntityManager entityManager;
public ThingRepository(EntityManager entityManager)
super(Thing.class, entityManager);
this.entityManager=entityManager;
@Transactional
public List save(List things)
things.forEach(thing -> entityManager.persist(thing));
return things;
El código anterior se basa en los siguientes enlaces:
- http://www.hameister.org/SpringBootUsingIdsForBulkImports.html
- http://www.hameister.org/SpringBootBulkImportWithCrudRepository.html
- https://vladmihalcea.com/la-mejor-manera-de-hacer-procesamiento-por-lotes-con-jpa-e-hibernate/
Puede configurar Hibernate para hacer DML masivo. Eche un vistazo a Spring Data JPA: inserciones/actualizaciones masivas simultáneas. Creo que la sección 2 de la respuesta podría resolver su problema:
Habilite el procesamiento por lotes de declaraciones DML Habilitar el soporte de procesamiento por lotes daría como resultado una menor cantidad de viajes de ida y vuelta a la base de datos para insertar/actualizar la misma cantidad de registros.
Citando declaraciones INSERT y UPDATE por lotes:
hibernate.jdbc.batch_size = 50
hibernate.order_inserts = true
hibernate.order_updates= true
hibernate.jdbc.batch_versioned_data= true
ACTUALIZAR: Tienes que configurar las propiedades de hibernación de manera diferente en tu application.properties
expediente. Están bajo el espacio de nombres: spring.jpa.properties.*
. Un ejemplo podría verse como el siguiente:
spring.jpa.properties.hibernate.jdbc.batch_size = 50
spring.jpa.properties.hibernate.order_inserts = true
....