2012-03-15 27 views
5

Mi aplicación se basa en Hibernate 3.2 y Spring 2.5. Aquí está el fragmento de gestión de transacciones relacionadas con el contexto de aplicación:Inserciones por lotes con Hibernate & Spring

<tx:annotation-driven transaction-manager="txManager"/> 
    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
      <property name="sessionFactory" ref="sessionFactory"/> 
      <property name="nestedTransactionAllowed" value="true"/> 
    </bean> 
    <bean id="transactionTemplate" classs="org.springframework.transaction.support.TransactionTemplate"> 
      <property name="transactionManager" ref="txManager"/> 
    </bean> 
    <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/> 
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
    <property name="configLocation" value="classpath:/hibernate.cfg.xml"></property> 
    </bean> 

Para todos hay de clase de servicio correspondiente y las transacciones se manejan allí utilizando @Transactional en cada método en la capa de servicio de la DAO. Sin embargo, hay un escenario ahora que un método en DAO dice "parse()" se llama desde la capa de servicio. En la capa de servicio, especifiqué @Transactional(readOnly=false). Este método de análisis en el DAO llama a otro método que dice "guardar()" en el mismo DAO que almacena una gran cantidad de filas (alrededor de 5000) en la base de datos. Ahora el método de guardar se llama en un bucle desde la función de análisis. Ahora el problema es que después de alrededor de 100 llamadas al método "guardar" ... a veces recibo una Excepción de OutOfMemory o algunas veces el programa deja de responder.

Por ahora estos son los cambios que he hecho con el método de ahorrar:

Session session = getHibernateTemplate().getSessionFactory().openSession(); 
      Transaction tx = session.beginTransaction(); 

      int counter = 0; 
      if(books!=null && !books.isEmpty()){ 
       for (Iterator iterator = books.iterator(); iterator 
         .hasNext();) { 
        Book book = (Book) iterator.next(); 
        session.save(book); 
        counter++; 
        if(counter % 20==0) { 
         session.flush(); 
         session.clear(); 
        } 
       } 
      } 
      tx.commit(); 
     session.close(); 

Este es el único método en mi aplicación donde comienzo de una transacción como esta y COMPROMÉTANSE al final del método. De lo contrario, normalmente solo llamo al getHibernateTemplate.save(). No estoy seguro de si debo realizar la gestión de transacciones para este método de guardado por separado en el DAO al colocar @Transactional(readOnly=false, PROPOGATION=NEW) en save(), ¿o está bien este enfoque?

También he actualizado el hibernate.jdbc.batch_size a 20 en el archivo de configuración hibernate.cfg.

¿Alguna sugerencia?

Respuesta

0

Haría refactorizar parse de una manera que no llame directamente al save, sino que tome alguna devolución de llamada desde la capa de servicio. La capa de servicio pasaría su método transaccional con la llamada save como esta devolución de llamada.

Puede que no funcione exactamente como se describe en su caso, pero a partir de esta breve descripción, sería algo que probaría.

1

Solo necesita el bit con el enjuague y la limpieza de la sesión. Deje la gestión de transacciones en Spring. Utilice sessionFactory.getCurrentSession() para llegar a la sesión que Spring ya ha abierto para usted. Además, la reciente recomendación de Spring es evitar HibernateTemplate y trabajar directamente con la API de Hibernate. Inyecte SessionFactory en su dao-bean.

5

Para la inserción por lotes con hibernación, la mejor práctica es StatelessSession, doesn `t caché de los estados de su entidad, que no se encontrará OutOfMemory, el código como:

if (books == null || books.isEmpty) { 
    return; 
} 
StatelessSession session = getHibernateTemplate().getSessionFactory().openStatelessSession(); 
Transaction tx = session.beginTransaction(); 

for (Book each : books) {   
    session.insert(book);   
} 
tx.commit(); 
session.close(); 

y la transacción de StatelessSession es independiente del contexto de transacción actual.

Cuestiones relacionadas