2010-03-07 8 views
7

Estoy usando hibernate para actualizar productos 20K en mi base de datos.usando Hibernate para cargar productos 20K, modificando la entidad y actualizando a db

A partir de ahora estoy incorporando los productos 20K, recorriendo y modificando algunas propiedades y luego actualizando la base de datos.

manera:

load products 

foreach products 
    session begintransaction 
    productDao.MakePersistant(p); 
    session commit(); 

A partir de ahora las cosas son bastante lento en comparación con el estándar JDBC, ¿qué puedo hacer para acelerar las cosas?

Estoy seguro de que estoy haciendo algo mal aquí.

+0

¿De verdad quiso decir * N * Hibernate (en el título)? – M4N

+0

No es para Java - NHibernate es para .NET. – duffymo

+0

arregló el título gracias. – Blankman

Respuesta

9

El lugar adecuado para mirar en el la documentación para este tipo de tratamiento es el total Chapter 13. Batch processing.

Aquí, hay varios errores evidentes en su enfoque actual:

  • usted no debe comenzar/confirmar la transacción para cada actualización.
  • debe activar JDBC de procesamiento por lotes y la puso a un número razonable (10-50):

    hibernate.jdbc.batch_size 20 
    
  • que debiera flush() y luego clear() la sesión a intervalos regulares (cada n registros en los que n es igual a el parámetro hibernate.jdbc.batch_size) o seguirá creciendo y puede explotar (con un OutOfMemoryException) en algún momento.

A continuación, el ejemplo dado en la sección 13.2. Batch updates ilustrar esto:

Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 

ScrollableResults customers = session.getNamedQuery("GetCustomers") 
    .setCacheMode(CacheMode.IGNORE) 
    .scroll(ScrollMode.FORWARD_ONLY); 
int count=0; 
while (customers.next()) { 
    Customer customer = (Customer) customers.get(0); 
    customer.updateStuff(...); 
    if (++count % 20 == 0) { 
     //flush a batch of updates and release memory: 
     session.flush(); 
     session.clear(); 
    } 
} 

tx.commit(); 
session.close(); 

También puede considerar el uso de la StatelessSession.

Otra opción sería usar DML-style operations (en HQL!): UPDATE FROM? EntityName (WHERE where_conditions)?.Este ejemplo UPDATE HQL:

Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 

String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName"; 
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName"; 
int updatedEntities = s.createQuery(hqlUpdate) 
     .setString("newName", newName) 
     .setString("oldName", oldName) 
     .executeUpdate(); 
tx.commit(); 
session.close(); 

Una vez más, consulte la documentación de los detalles (especialmente cómo tratar con los version o timestamp valores de las propiedades utilizando la palabra clave VERSIONED).

5

Si esto es pseudo-código, me gustaría recomendar la transacción se mueve fuera del bucle, o al menos tener un bucle doble si es que tiene todos los 20K de productos en una sola transacción es demasiado:

load products 
foreach (batch) 
{ 
    try 
    { 
     session beginTransaction() 
     foreach (product in batch) 
     { 
      product.saveOrUpdate() 
     } 
     session commit() 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace() 
     session.rollback() 
    } 
} 

también , Recomendaría que agregue sus ACTUALIZACIONES en lugar de enviarlas individualmente a la base de datos. Hay demasiado tráfico de red de esa manera. Agrupe cada pedazo en un solo lote y envíelos todos a la vez.

0

La forma más rápida de realizar una actualización por lotes sería convertirla a una sola instrucción SQL y ejecutarla como sql sin procesar en la sesión. Algo así como

update TABLE set (x=y) where w=z; 

En su defecto se puede tratar de hacer menos transacciones y hacer cambios en lotes:

start session 
start transaction 

products = session.getNamedQuery("GetProducs") 
    .setCacheMode(CacheMode.IGNORE) 
    .scroll(ScrollMode.FORWARD_ONLY); 
count=0; 
foreach product 
    update product 
    if (++count % 20 == 0) { 
     session.flush(); 
     session.clear(); 
    } 
} 

commit transaction 
close session 

Para más información mira el Hibernate Community Docs

1

Estoy de acuerdo con la respuesta anterior acerca de mirar el capítulo sobre procesamiento por lotes.

También quería agregar que debe asegurarse de que solo carga lo necesario para los cambios que necesita realizar para el producto.

Lo que quiero decir es que si el producto carga ansiosamente una gran cantidad de otros objetos que no son importantes para esta transacción, debería considerar no cargar los objetos unidos, esto acelerará la carga de productos y dependiendo de su persistencia estrategia, también puede ahorrarle tiempo al hacer que el producto sea persistente de nuevo.

Cuestiones relacionadas