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


Si tengo una anotación @Transactional en un método privado en un frijol de primavera, ¿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();
Author: Juha Syrjälä, 2010-12-09

6 answers

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

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

Visibilidad del método y @Transactional

Cuando use proxies, debe aplicar la anotación @Transactional solamente a métodos con visibilidad pública. Si usted hace anotaciones protegidas, privadas o paquete - métodos visibles con el @Transactional anotación, sin error se plantea, pero el método anotado no muestra la configuración ajustes transaccionales. Considere la uso de AspectJ (ver abajo) si necesita para anotar métodos no públicos.

 186
Author: skaffman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-02 01:34:07

La Pregunta no es privada o pública, la pregunta es: ¿Cómo se invoca y qué implementación de AOP se utiliza?

Si utiliza (por defecto) Spring Proxy AOP, entonces toda la funcionalidad 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:

  • Porque los métodos privados no deben ser invocados desde otro frijol (la excepción es la reflexión), 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 instrucción solo es correcta si se usa (por defecto) Spring Proxy AOP).

@ See Spring Reference: Chapter 9.6 9.6 Proxying mechanisms

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

 98
Author: Ralph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-05 08:33:38

De forma predeterminada, el atributo @Transactional solo funciona 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 será:

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

Referencia de primavera: Usando @Transactional

Nota: En el modo proxy (que es el predeterminado), solo se interceptarán las llamadas a métodos 'externos' que ingresen a través del proxy. Esto significa que la 'auto invocación', es decir, un método dentro del objeto de destino llamar a algún otro método del objeto de destino, no conducirá a una transacción real en tiempo de ejecución, incluso si el método invocado está marcado con @Transactional!

Considere el uso del modo AspectJ (vea más abajo) si espera que las auto invocaciones también estén envueltas con transacciones. En este caso, no habrá un proxy en primer lugar; en su lugar, la clase objetivo será 'tejida' (es decir, se modificará su código de bytes) para convertir @Transactional en un comportamiento de tiempo de ejecución en cualquier tipo de método.

 26
Author: Juha Syrjälä,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-09 09:44:50

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

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

Primero, agregue la dependencia para aspectjrt.

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.8</version>
</dependency>

Luego agregue el complemento AspectJ para hacer el tejido real de bytecode en Maven (esto puede no ser un ejemplo mínimo).

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.8</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

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 a este enfoque: Tendrá que configurar su IDE para ser consciente de AspectJ de lo contrario, si ejecuta la aplicación a través de Eclipse, por ejemplo, puede no funcionar. Asegúrese de probar contra una compilación Maven directa como una control de cordura.

 8
Author: James Watkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-28 20:21:36

Por favor vea este documento:

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

Considere el uso del modo AspectJ (consulte el atributo mode en la tabla siguiente) si espera que las auto invocaciones sean envuelto con transacciones también. En este caso, no habrá un proxy en primer lugar; en su lugar, la clase objetivo se tejerá (es decir, se modificará su código de bytes) para convertir @Transaccional en comportamiento de tiempo de ejecución en cualquier tipo de método.

----------------------------más de

Entonces, otra forma es user BeanSelfAware

 3
Author: user536161,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-09 09:15:19

La respuesta es no. Consulte Referencia de primavera: Usando @Transactional :

La anotación @Transactional puede colocarse 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

 3
Author: ivy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-09 09:46:13