¿Método @Transactional llamando a otro método sin anotación @Transactional?

89

He visto un método en una clase de servicio que estaba marcado como @Transactional, pero también llamaba a algunos otros métodos en esa misma clase que no estaban marcados como @Transactional.

¿Significa que la llamada a métodos separados está provocando que la aplicación abra conexiones separadas a la base de datos o suspenda la transacción principal, etc.?

¿Cuál es el comportamiento predeterminado de un método sin anotaciones al que llama otro método con @Transactionalanotación?

goe
fuente

Respuestas:

117

Cuando llama a un método que no está @Transactionaldentro de un bloque de transacción, la transacción principal continuará con el nuevo método. Utilizará la misma conexión del método principal (con @Transactional) y cualquier excepción causada en el método llamado (sin, @Transactionalhará que la transacción se revierta como se configuró en la definición de transacción.

Si llama a un método con una @Transactionalanotación desde un método @Transactionaldentro de la misma instancia, entonces el comportamiento transaccional de los métodos llamados no tendrá ningún impacto en la transacción. Pero si llama a un método con una definición de transacción desde otro método con una definición de transacción, y están en diferentes instancias, entonces el código en el método llamado seguirá las definiciones de transacción dadas en el método llamado.

Puede encontrar más detalles en la sección Gestión declarativa de transacciones de la documentación de transacciones de primavera .

El modelo de transacción declarativa de Spring utiliza un proxy AOP. por lo que el proxy AOP es responsable de la creación de las transacciones. El proxy AOP estará activo solo si los métodos dentro de la instancia se llaman desde fuera de la instancia.

Arun P Johny
fuente
¿Es ese el comportamiento predeterminado de primavera?
ir
Si. Es el comportamiento predeterminado.
Arun P Johny
2
@Tomasz Sí. Pero también debe mencionarse que cambiar la propagación de transacciones en un método que se llama desde otro método @Transactional no tendrá ningún efecto.
Fil
1
@Tomasz, eso es lo que quise decir al decir will follow the transaction definitions given in the called method. Pero si la llamada proviene de la misma instancia de objeto, no tendrá ningún efecto, ya que la llamada no se propagará a través de los proxies aop que son responsables del mantenimiento de la transacción.
Arun P Johny
5
@Filip, eso no es completamente correcto. Si llama a un método con una @Transactionaldefinición de un objeto / instancia diferente, aunque el método de llamada tenga @Transactionalatributos diferentes , el método llamado seguirá su propia definición de transacción.
Arun P Johny
23
  • ¿Eso significa que la llamada a métodos separados está causando que la aplicación abra conexiones separadas a la base de datos o suspenda la transacción principal, etc.?

Eso depende del nivel de propagación . Aquí están todos los posibles valores de nivel .

Por ejemplo, en caso de que un nivel de propagación esté ANIDADO, una transacción actual se "suspenderá" y se creará una nueva transacción ( nota: la creación real de una transacción anidada solo funcionará en administradores de transacciones específicos )

  • ¿Cuál es el comportamiento predeterminado de un método sin anotaciones al que llama otro método con la anotación @Transactional?

El nivel de propagación predeterminado (lo que usted llama "comportamiento") es REQUERIDO . En caso de que se llame a un método "interno" que tenga una @Transactionalanotación (o se realice una transacción declarativa a través de XML), se ejecutará dentro de la misma transacción , por ejemplo, se crea "nada nuevo".

tolicio
fuente
¿Qué pasa con las subllamadas de NOT_SUPPORTED que no tienen ninguna anotación? ¿Hereda NOT_Supported o abrieron una nueva transacción ya que REQURED es el valor predeterminado? Por ejemplo: f1.call () {f2 ()} con la anotación NOT_SUPPORTED para f1 y non para f2.
Dave
8

@Transactional marca el límite de la transacción (inicio / finalización) pero la transacción en sí está vinculada al hilo. Una vez que se inicia una transacción, se propaga a través de las llamadas al método hasta que el método original regresa y la transacción se confirma / retrocede.

Si se llama a otro método que tiene una anotación @Transactional, entonces la propagación depende del atributo de propagación de esa anotación.

fuentedelica
fuente
Las 3 respuestas entran en conflicto entre sí en algún grado, no estoy seguro de cuál es más precisa.
Eric Wang
1
@EricWang Solo quería compartir que probé este escenario hoy y la respuesta de Arun P Johny (con comentarios) es la más precisa para este escenario de invocaciones internas .
Vinay Vissh
3

El método interno afectará al método externo si el método interno no está anotado con @Transactional.

En caso de que el método interno también esté anotado con @Transactional con REQUIRES_NEW, sucederá lo siguiente.

...
@Autowired
private TestDAO testDAO;

@Autowired
private SomeBean someBean;

@Override
@Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) {
  testDAO.insertUser(user);
  try{
    someBean.innerMethod();
  } catch(RuntimeException e){
    // handle exception
  }
}


@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() {
  throw new RuntimeException("Rollback this transaction!");
}

El método interno está anotado REQUIRES_NEWy arroja una RuntimeException por lo que establecerá su transacción para deshacer pero NO EFECTUARÁ la transacción externa. La transacción externa se PAUSA cuando comienza la transacción interna y luego SE REANUDA DESPUÉS de que se concluye la transacción interna. Se ejecutan de forma independiente entre sí, por lo que la transacción externa PUEDE confirmarse correctamente.

saran3h
fuente
1
Para aclarar a los principiantes, estoy bastante seguro de que innerMethod () debe estar en un bean diferente (también conocido como Objeto Java administrado por Spring) que outerMethod (). Si ambos están en el mismo bean, no creo que innerMethod realmente use el comportamiento transaccional declarado en su anotación. Más bien usará lo que se declara en la declaración del método externo (). Esto se debe a cómo Spring maneja AOP, que se usa para sus anotaciones @Transactional ( docs.spring.io/spring/docs/3.0.x/spring-framework-reference/… )
johnsimer