2010-06-14 12 views
39

tenemos:nueva transacción en el frijol de primavera

@Transactional(propagation = Propagation.REQUIRED) 
public class MyClass implementes MyInterface { ... 

MyInterface tiene un único método: go().

Cuando se ejecuta go() iniciamos una nueva transacción que confirma/revierte cuando se completa el método, esto está bien.

Digamos que en go() llamamos a un método privado en MyClass que tiene @Transactional(propagation = Propagation.REQUIRES_NEW. Parece que Spring "ignora" la anotación REQUIRES_NEW y no inicia una nueva transacción. Creo que esto se debe a que Spring AOP funciona en el nivel de interfaz (MyInterface) y no intercepta ninguna llamada a los métodos de MyClass. ¿Es esto correcto?

¿Hay alguna manera de comenzar una nueva transacción dentro de la transacción go()? ¿Es la única forma de llamar al otro Bean administrado por resorte que tiene transacciones configuradas como REQUERES_NEW?


actualización: Agregando que cuando los clientes ejecutan go() lo hacen a través de una referencia a la interfaz, no a la clase:

@Autowired 
MyInterface impl; 

impl.go(); 

Respuesta

66

De la referencia primavera 2,5:

Al utilizar proxies, la @Transactional anotación sólo debe ser aplicado a métodos con visibilidad pública. Si anota métodos visibles del paquete protegidos, privados o con la anotación @Transactional, no se generará ningún error , pero el método anotado no mostrará la configuración transaccional configurada.

So Spring ignora la anotación @Transactional en métodos no públicos.

También,

En el modo proxy (que es el valor predeterminado), único método 'externo' llamadas que vienen en a través del proxy será interceptada. Esto significa que 'auto invocación', es decir, un método dentro del objeto objetivo llamando a algún otro método del objeto objetivo, no conducirá a una transacción real en tiempo de ejecución incluso si el método invocado está marcado con @Transactional!

Así que incluso si hace su método public, al llamarlo desde un método de la misma clase no se iniciará una nueva transacción.

Puede usar el modo aspectj en la configuración de transacción para que el código relacionado con la transacción esté entrelazado en la clase y no se cree ningún proxy en el tiempo de ejecución.

Ver the reference document para más detalles.

Otra posible forma de hacerlo es ir a buscar el proxy de primavera de la clase en la propia clase y llamar a métodos en ella en lugar de this:

@Service 
@Transactional(propagation = Propagation.REQUIRED) 
public class SomeService { 

    @Autowired 
    private ApplicationContext applicationContext; 

    private SomeService getSpringProxy() { 
     return applicationContext.getBean(this.getClass()); 
    } 

    private void doSomeAndThenMore() { 
     // instead of 
     // this.doSometingPublicly(); 
     // do the following to run in transaction 
     getSpringProxy().doSometingPublicly(); 
    } 

    public void doSometingPublicly() { 
     //do some transactional stuff here 
    } 

} 
37

@Transactional solamente se notará si es en un método public, debido a la forma en que funciona Spring AOP.

Sin embargo, puede programmatically start a new transaction si lo desea, utilizando TransactionTemplate, p. Ej.

TransactionTemplate txTemplate = new TransactionTemplate(txManager);     
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
txTemplate.execute(new TransactionCallback<Object>() { 
    public Object doInTransaction(TransactionStatus status) { 
     // do stuff 
    } 
}); 
+0

Pero incluso si '@ Transactional' está en un método público de MyClass, todavía parece que Spring no lo recogerá ** a menos que ** el método esté definido en la interfaz, ¿correcto? –

+0

Para agregar, esto se debe a que el cliente que está ejecutando el método go() en cuestión tiene una referencia a la interfaz, no a la clase. –

+3

@Marcus: más o menos. Si 'MyClass' implementa una interfaz, Spring solo usará esa interfaz para generar el proxy transaccional e ignorará incluso los métodos públicos que no sean de interfaz. Sin embargo, si 'MyClass' no implementa ninguna interfaz, entonces se usarán todos los métodos públicos. – skaffman

3

En resumen se tiene que llamar al método embargo proxy para lograr el comportamiento transaccional. Es posible llamar a un "REQUIRES_NEW" en el mismo bean, como se le preguntó en la pregunta. Para hacer eso, debes hacer una referencia "propia". En primavera no es sencillo. Debe inyectarlo con la anotación @Resource.

@Service("someService") 
public class ServieImpl implements Service { 

    @Resource(name = "someService") 
    Service selfReference; 

    @Transactional 
    public void firstMethod() { 
     selfReference.secondMethod(); 
    } 

    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    public void secondMethod() {  
     //do in new transaction 
    } 

} 

La invocación de firstMethod llama al proxy y no "esto" lo que debería hacer la transacción "REQUIRES_NEW" para trabajar.

Cuestiones relacionadas