mvc example java spring hibernate

java - example - ¿Por qué @Transactional(propagation=Propagation.SUPPORTS, readOnly=true) corrige Hibernate Lazy Loading Exceptions?



spring jpa hibernate (2)

Cambié una aplicación en la que estoy trabajando desde el uso del tejido de tiempo de carga de AspectJ al uso de proxies Spring CGlib y, justo después de hacerlo, había muchas partes del código en el que comencé a obtener excepciones de carga perezosa de hibernación donde en el pasado no había excepciones arrojado

He podido resolver estas excepciones de carga diferida agregando @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) a un grupo de métodos previamente públicos que no tenían ningún atributo transaccional pero llamaron repositorios Spring para leer datos de la base de datos .

¿Alguien sabe por qué agregar @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) elimina las excepciones de carga perezosa en hibernación y por qué estas anotaciones no fueron necesarias con el tejido de tiempo de carga de AspectJ pero se requieren sin ellas?

Actualización 2 Creo que la eliminación de AspectJ NO era el problema , pero el problema era que realmente no entendía el comportamiento real de la propagación de SUPPORTS. En particular, la forma en que SUPPORTS interactuó con el EntityManager de JPA y, por lo tanto, eliminé un grupo de propagación de SUPPORTS que causó las excepciones de carga lenta. Después de leer el código fuente de Spring Transaction Manager, todo quedó claro en cuanto a qué hacer. La idea clave de que la documentación de Spring no señala realmente bien es que las anotaciones @Transactional se utilizan como puntos de sincronización que vinculan el ciclo de vida de un EntityManager al inicio y al final de un método transaccional. También recomiendo esta serie de artículos en http://www.ibm.com/developerworks/java/library/j-ts1/ y esta publicación de blog http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/

Actualización 1

Este no es un caso de llamadas a métodos privados @Transactional que no pasan por el proxy AOP. Estos problemas están ocurriendo con métodos públicos que están siendo llamados desde otros servicios.

Aquí hay un ejemplo de la estructura del código, donde veo que ocurre el problema.

@Service public class FooService { @Autowired private BarService barService; public void someMethodThatOnlyReads() { SomeResult result = this.barService.anotherMethodThatOnlyReads() // the following line blows up with a HibernateLazyLoadingEcxeption // unless there is a @Transactional supports annotation on this method result.getEntity().followSomeRelationship(); } } @Service public class BarService { @Autowired private BarRepository barRepo; public SomeResult anotherMethodThatOnlyReads() { SomeEntity entity = this.barRepo.findSomeEntity(1123); SomeResult result = new SomeResult(); result.setEntity(entity); return result; } } @Repository public class BarRepository { @PersistenceContext private EntityManager em; public SomeEntity findSomeEntity(id Integer) { em.find(SomeEntity.class,id); } }


No estoy completamente seguro de por qué sucede, pero mi teoría es la siguiente.

Cuando pasa de AspectJ weaving a CGLIB proxies, @Transactional anotaciones @Transactional colocadas en los métodos llamados desde el mismo objeto dejan de tener efecto. Significa que el código dentro de estos métodos se ejecutará de manera no transaccional (a menos que tenga otro método @Transacional en su pila de llamadas donde @Transacional realmente surta efecto).

Javadoc para la Propagation.SUPPORTS dice:

Nota: Para los administradores de transacciones con sincronización de transacciones, PROPAGATION_SUPPORTS es ligeramente diferente de ninguna transacción, ya que define el alcance de la transacción para el que se aplicará la sincronización. Como consecuencia, los mismos recursos (conexión JDBC, sesión de hibernación, etc.) se compartirán para todo el ámbito especificado. Tenga en cuenta que esto depende de la configuración de sincronización real del administrador de transacciones.

Por lo tanto, cuando su código ejecuta una Session hibernación no transaccional utilizada para cargar objetos, no estará disponible para la posterior inicialización de propiedades perezosas. Al anotar el método de nivel superior en su pila de código con @Transactional(propagation = Propagation.SUPPORTS) , Hibernate Session estará disponible hasta que deje ese método.


Supongo que su código no está utilizando OpenSessionInViewFilter o algo similar.

Sin la anotación @Transactional , la sesión de Hibernate se cierra después de dejar el método BarRepository.findSomeEntity() .

Cuando se llama a un método @Transactional y el TransactionalInterceptor está correctamente vinculado al método (a través del proxy cglib o cualquier otra configuración AOP que tenga en el contexto de Spring), entonces la sesión se mantiene abierta por Spring durante la totalidad del método anotado, evitando así cualquier excepción de carga perezosa.

Si org.springframework.transaction el registro en DEBUG en los org.springframework.transaction y org.springframework.orm.hibernate3 (o hibernate4 si está en Hibernate 4), en particular la clase HibernateTransactionManager y org.springframework.transaction.support.AbstractPlatformTransactionManager debe ver exactamente en qué puntos del flujo de código Spring está decidiendo que necesita abrir y cerrar la sesión de Hibernate. Los registros también deben mostrar por qué una sesión o transacción se abre / cierra en cada punto.