2011-02-24 23 views
18

En Spring, un método anotado con @Transactional obtendrá una nueva transacción si aún no hay una, pero noté que un método transaccional no obtiene ninguna transacción transacción si se llama desde una no transaccional. Aquí está el código.El método @Transactional llamado desde otro método no obtiene una transacción

@Component 
public class FooDao { 
    private EntityManager entityManager; 

    @PersistenceContext 
    protected void setEntityManager(EntityManager entityManager) { 
     this.entityManager = entityManager; 
    } 

    @Transactional 
    public Object save(Object bean) { 
     return this.entityManager.merge(bean); 
    } 

    public Object saveWrap(Object bean) { 
     return save(bean); 
    } 
} 

@Component 
public class FooService { 
    private FooDao fooDao; 

    public void save(Object bean) { 
     this.fooDao.saveWrap(bean); // doesn't work. 
     this.fooDao.save(bean); // works 
    } 
} 

saveWrap() es un método regular que llama save() que es transaccional, pero saveWrap() no persistirá cualquier cambio.

Estoy usando Spring 3 e Hibernate 3. ¿Qué estoy haciendo mal aquí? Gracias.

Respuesta

30

Es una de las limitaciones de Springs AOP. Como el dao bean es, de hecho, un proxy cuando se crea en primavera, significa que llamar a un método desde dentro de la misma clase no llamará al consejo (que es la transacción). Lo mismo ocurre con cualquier otro punto

+2

No hay ningún problema al anotar el método saveWrap con @Transactional. El comportamiento predeterminado con la propagación de transacciones es OBLIGATORIO, lo que significa que si tuviera que obtener una transacción anidada (es decir, está en una transacción y luego llama a otro método que también es @Transactional) sería simplemente usar la transacción existente y NO crear otra si eso es lo que le teme) –

+0

¿qué hay de usar cglib para proxy? Recuerdo el consejo del proxy cglib cada método, incluso si lo llamas dentro de la misma clase. – hiway

11

Sí, este es el comportamiento esperado. @Transactional le dice a Spring que cree un proxy alrededor del objeto. El proxy intercepta las llamadas al objeto desde otros objetos. El proxy no intercepta llamadas dentro del objeto.

Si desea hacer esto, agregue @Transactional en el método que se invoca desde "afuera".

+1

Otra opción sería marcar toda la clase como transaccional colocando @Transactional() (con posiblemente readOnly = true/false, propagation = something etc.) en la parte superior de la clase y luego anulando readOnly y/o propagation- valores por método según sea necesario. – esaj

2

Esto es un poco tarde, lo sé, pero me gustaría añadir una forma de superar esta limitación, es que dentro del método obtenga el bean de primavera desde el contexto de la aplicación e invoque el método. Cuando el bean de primavera se obtiene del contexto de la aplicación, será el bean proxy, no el bean original. Como el bean proxy ahora invoca el método en lugar del bean original, se implementará el aviso de transacción.

Cuestiones relacionadas