¿El atributo Spring @Transactional funciona en un método privado?

Si tengo una @Transactional -annotation en un método privado en un Spring Bean, ¿la anotación tiene algún efecto?

Si la anotación @Transactional está en un método público, funciona y abre una transacción.

 public class Bean { public void doStuff() { doPrivateStuff(); } @Transactional private void doPrivateStuff() { } } ... Bean bean = (Bean)appContext.getBean("bean"); bean.doStuff(); 

La respuesta a su pregunta es no: @Transactional no tendrá efecto si se usa para anotar métodos privados. El generador de proxy los ignorará.

Esto está documentado en Spring Manual capítulo 10.5.6 :

Visibilidad del método y @Transactional

Al usar proxies, debe aplicar la anotación @Transactional solo a los métodos con visibilidad pública. Si anota métodos protegidos, privados o visibles del paquete con la anotación @Transactional , no se @Transactional ningún error, pero el método anotado no muestra la configuración transaccional configurada. Considere el uso de AspectJ (ver a continuación) si necesita anotar métodos no públicos.

La pregunta no es privada ni pública, la pregunta es: ¿cómo se invoca y qué implementación de AOP se usa?

Si usa (predeterminado) Spring Proxy AOP, entonces toda la funcionalidad de AOP proporcionada por Spring (como @Transational ) solo se tendrá en cuenta si la llamada pasa por el proxy. – Este es normalmente el caso si el método anotado se invoca desde otro bean.

Esto tiene dos implicaciones:

  • Como los métodos privados no se deben invocar desde otro bean (la excepción es el reflection), su anotación @Transactional no se tiene en cuenta.
  • Si el método es público, pero se invoca desde el mismo bean, tampoco se tendrá en cuenta (esta afirmación solo es correcta si (por defecto) se utiliza Spring Proxy AOP).

@Ver referencia de spring: Capítulo 9.6 9.6 Mecanismos de proxies

En mi humilde opinión, debe usar el modo aspectJ, en lugar de Spring Proxies, que resolverá el problema. Y los aspectos transaccionales de AspectJ se tejen incluso en métodos privados (verificados para Spring 3.0).

Por defecto, el atributo @Transactional funciona solo cuando se llama a un método anotado en una referencia obtenida de applicationContext.

 public class Bean { public void doStuff() { doTransactionStuff(); } @Transactional public void doTransactionStuff() { } } 

Esto abrirá una transacción:

 Bean bean = (Bean)appContext.getBean("bean"); bean.doTransactionStuff(); 

Esto no:

 Bean bean = (Bean)appContext.getBean("bean"); bean.doStuff(); 

Referencia de spring: uso de @Transactional

Nota: En el modo proxy (que es el predeterminado), solo las llamadas al método ‘externo’ que entren a través del proxy serán interceptadas. Esto significa que la “auto invocación”, es decir, un método dentro del objeto objective que llama a otro método del objeto objective, no dará lugar a una transacción real en el tiempo de ejecución, incluso si el método invocado está marcado con @Transactional .

Considere el uso del modo AspectJ (ver a continuación) si espera que las autovocaciones se envuelvan con las transacciones también. En este caso, no habrá un proxy en primer lugar; en su lugar, la clase objective será ‘tejida’ (es decir, se modificará su código de bytes) para convertir @Transactional en comportamiento en tiempo de ejecución en cualquier tipo de método.

Sí, es posible usar @Transactional en métodos privados, pero como otros lo han mencionado, esto no funcionará de la caja. Necesitas usar AspectJ. Me llevó algo de tiempo descubrir cómo hacerlo funcionar. Compartiré mis resultados.

Elegí usar el tejido en tiempo de comstackción en lugar del tejido en tiempo de carga porque creo que es una mejor opción general. Además, estoy usando Java 8 por lo que es posible que deba ajustar algunos parámetros.

Primero, agregue la dependencia para aspectjrt.

  org.aspectj aspectjrt 1.8.8  

A continuación, agregue el complemento AspectJ para realizar el tejer bytecode real en Maven (este puede no ser un ejemplo mínimo).

  org.codehaus.mojo aspectj-maven-plugin 1.8  1.8 1.8 1.8   org.springframework spring-aspects       compile     

Finalmente agregue esto a su clase de configuración

 @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) 

Ahora debería poder usar @Transactional en métodos privados.

Una advertencia para este enfoque: deberá configurar su IDE para conocer AspectJ; de lo contrario, si ejecuta la aplicación a través de Eclipse, es posible que no funcione. Asegúrate de probar contra una construcción directa de Maven como un control de cordura.

Plese vea este documento:

En el modo proxy (que es el predeterminado), solo se interceptan las llamadas a métodos externos que ingresan a través del proxy. Esto significa que la auto invocación, en efecto, un método dentro del objeto objective que llama a otro método del objeto objective, no conducirá a una transacción real en el tiempo de ejecución, incluso si el método invocado está marcado con @Transactional.

Considere el uso del modo AspectJ (vea el atributo de modo en la tabla a continuación) si espera que las autovocaciones se envuelvan con transacciones también. En este caso, no habrá un proxy en primer lugar; en su lugar, la clase objective será tejida (es decir, se modificará su código de bytes) para convertir @Transactional en el comportamiento de tiempo de ejecución en cualquier tipo de método.

—————————-encima

así que, otra manera es el usuario BeanSelfAware

La respuesta es no. Por favor, consulte la referencia de spring: Uso de @Transactional :

La anotación @Transactional se puede colocar antes de una definición de interfaz, un método en una interfaz, una definición de clase o un método público en una clase