java - que - invernacion
¿Inserto a granel o actualización con hibernación? (6)
De acuerdo con una respuesta a una pregunta similar , se puede hacer configurando Hibernate para insertar objetos usando un procedimiento almacenado personalizado que utiliza la funcionalidad de upsert su base de datos. Aunque no es bonito
Necesito consumir una gran cantidad de datos de un archivo CSV diario. El CSV contiene alrededor de 120K registros. Esto se está reduciendo a un rastreo cuando se utiliza la hibernación. Básicamente, parece que hibernate está haciendo un SELECT antes de cada INSERT (o UPDATE) cuando se usa saveOrUpdate (); para cada instancia que se conserva con saveOrUpdate (), se emite un SELECT antes del INSERT real o una ACTUALIZACIÓN. Puedo entender por qué está haciendo esto, pero es terriblemente ineficiente para hacer el procesamiento en masa, y estoy buscando alternativas
Confío en que el problema de rendimiento radica en la forma en que uso hibernación para esto, ya que tengo otra versión que funciona con SQL nativo (que analiza el CSV de la misma manera) y su forma literal de círculos alrededor de esta nueva versión)
Entonces, para la pregunta real, ¿existe una sintaxis de hibernación alternativa a mysqls "INSERT ... ON DUPLICATE"?
O, si elijo hacer SQL nativo para esto, ¿puedo hacer SQL nativo dentro de una transacción de hibernación? ¿Significará, apoyará el commit / rollbacks?
Desde Hibernate Batch Processing Para actualización he usado lo siguiente:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE")
.scroll();
int count = 0;
while ( employeeCursor.next() ) {
Employee employee = (Employee) employeeCursor.get(0);
employee.updateEmployee();
seession.update(employee);
if ( ++count % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Pero para insertar me gustaría ir por la respuesta jcwayne
Hay muchos posibles cuellos de botella en las operaciones a granel. El mejor enfoque depende en gran medida de cómo se ven sus datos. Eche un vistazo a la sección del Manual de Hibernate sobre el procesamiento por lotes.
Como mínimo, asegúrese de estar utilizando el siguiente patrón (copiado del manual):
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Si está asignando un archivo plano a un gráfico de objetos muy complejo, es posible que tenga que ser más creativo, pero el principio básico es que debe encontrar un equilibrio entre la inserción de grandes porciones de datos en la base de datos con cada descarga / confirmación y evitar explotando el tamaño de la caché de nivel de sesión.
Por último, si no necesita que Hibernate maneje ninguna colección o cascada para que sus datos se inserten correctamente, considere la posibilidad de usar StatelessSession .
La selección "extra" es generar el identificador único para sus datos.
Cambie a la generación de secuencia HiLo y puede reducir los viajes de ida y vuelta a la base de datos en la cantidad del tamaño de asignación. Tenga en cuenta que habrá un espacio en las claves primarias a menos que ajuste el valor de secuencia para el generador HiLo
Si solo desea importar datos sin realizar ningún procesamiento o transformación, una herramienta como PostgreSQL COPY
es la forma más rápida de importar datos.
Sin embargo, si necesita realizar la transformación, la agregación de datos, la correlación / fusión entre los datos existentes y los entrantes, entonces necesita procesamiento por lotes a nivel de la aplicación.
En este caso, como expliqué en este artículo , usted desea limpiar-borrar-confirmar regularmente:
int entityCount = 50;
int batchSize = 25;
EntityManager entityManager = entityManagerFactory()
.createEntityManager();
EntityTransaction entityTransaction = entityManager
.getTransaction();
try {
entityTransaction.begin();
for (int i = 0; i < entityCount; i++) {
if (i > 0 && i % batchSize == 0) {
entityTransaction.commit();
entityTransaction.begin();
entityManager.clear();
}
Post post = new Post(
String.format("Post %d", i + 1)
);
entityManager.persist(post);
}
entityTransaction.commit();
} catch (RuntimeException e) {
if (entityTransaction.isActive()) {
entityTransaction.rollback();
}
throw e;
} finally {
entityManager.close();
}
Además, asegúrese de habilitar los procesos por lotes de JDBC también utilizando las siguientes propiedades de configuración:
<property
name="hibernate.jdbc.batch_size"
value="25"
/>
<property
name="hibernate.order_inserts"
value="true"
/>
<property
name="hibernate.order_updates"
value="true"
/>
Para obtener más detalles sobre estas propiedades de configuración de Hibernate, consulta este artículo .
Si usa una secuencia o un generador nativo, Hibernate usará una selección para obtener la identificación:
<id name="id" column="ID">
<generator class="native" />
</id>
Debes utilizar el generador de hilo o seqHiLo:
<id name="id" type="long" column="id">
<generator class="seqhilo">
<param name="sequence">SEQ_NAME</param>
<param name="max_lo">100</param>
</generator>
</id>