2023-05-03 11:06:20 +02:00
== Why is this an issue?
2023-07-21 18:15:07 +02:00
Transactional methods have a propagation type parameter in the @Transaction annotation that specifies the requirements about the transactional context in which the method can be called and how it creates, appends, or suspends an ongoing transaction.
2021-04-28 16:49:39 +02:00
2023-07-21 18:15:07 +02:00
When an instance that contains transactional methods is injected, Spring uses proxy objects to wrap these methods with the actual transaction code.
2021-04-28 16:49:39 +02:00
2023-07-21 18:15:07 +02:00
However, if a transactional method is called from another method in the same class, the `this` argument is used as the receiver instance instead of the injected proxy object, which bypasses the wrapper code.
This results in specific transitions from one transactional method to another, which are not allowed:
2021-04-28 16:49:39 +02:00
2021-06-03 16:43:28 +02:00
[frame=all]
[cols="^1,^1"]
|===
|From|To
| non-``++@Transactional++`` | MANDATORY, NESTED, REQUIRED, REQUIRES_NEW
| MANDATORY | NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
| NESTED | NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
| NEVER | MANDATORY, NESTED, REQUIRED, REQUIRES_NEW
| NOT_SUPPORTED | MANDATORY, NESTED, REQUIRED, REQUIRES_NEW
| REQUIRED or ``++@Transactional++`` | NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
| REQUIRES_NEW | NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
| SUPPORTS | MANDATORY, NESTED, NEVER, NOT_SUPPORTED, REQUIRED, REQUIRES_NEW
|===
2021-04-28 18:08:03 +02:00
2023-07-21 18:15:07 +02:00
== How to fix it
2021-04-28 16:49:39 +02:00
2023-07-21 18:15:07 +02:00
Change the corresponding functions into a compatible propagation type.
=== Code examples
==== Noncompliant code example
[source,java,diff-id=1,diff-type=noncompliant]
----
public void doTheThing() {
// ...
actuallyDoTheThing(); // Noncompliant, call from non-transactional to transactional
}
@Transactional
public void actuallyDoTheThing() {
// ...
}
----
==== Compliant solution
[source,java,diff-id=1,diff-type=compliant]
----
@Transactional
public void doTheThing() {
// ...
actuallyDoTheThing(); // Compliant
}
@Transactional
public void actuallyDoTheThing() {
// ...
}
2021-04-28 16:49:39 +02:00
----
2023-07-21 18:15:07 +02:00
==== Noncompliant code example
[source,java,diff-id=2,diff-type=noncompliant]
----
@Transactional
2021-04-28 16:49:39 +02:00
public void doTheThing() {
// ...
2023-07-21 18:15:07 +02:00
actuallyDoTheThing(); // Noncompliant, call from REQUIRED to REQUIRES_NEW
2021-04-28 16:49:39 +02:00
}
2023-07-21 18:15:07 +02:00
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void actuallyDoTheThing() {
// ...
}
----
==== Compliant solution
[source,java,diff-id=2,diff-type=compliant]
----
2021-04-28 16:49:39 +02:00
@Transactional
2023-07-21 18:15:07 +02:00
public void doTheThing() {
// ...
actuallyDoTheThing(); // Compliant, call from REQUIRED to MANDATORY
}
@Transactional(propagation = Propagation.MANDATORY)
2021-04-28 16:49:39 +02:00
public void actuallyDoTheThing() {
// ...
}
----
2021-04-28 18:08:03 +02:00
2023-07-21 18:15:07 +02:00
== Resources
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html[Spring Framework 6 API: Enum Class propagation]
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html[Spring Framework 6 API: Annotation Interface Transactional]
- https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/tx-propagation.html[Spring 6 Documentation: Transaction Propagation]
=== Articles & blog posts
- https://www.baeldung.com/spring-transactional-propagation-isolation[Baeldung: Transaction Propagation and Isolation in Spring @Transactional]
- https://dzone.com/articles/spring-transaction-propagation[DZone: Spring Transaction Propagation in a Nutshell]
2021-06-02 20:44:38 +02:00
2021-06-03 09:05:38 +02:00
ifdef::env-github,rspecator-view[]
2021-09-20 15:38:42 +02:00
'''
== Implementation Specification
(visible only on this page)
2023-05-25 14:18:12 +02:00
=== Message
"xxx's" @Transactional requirement is incompatible with the one for this method.
=== Highlighting
* primary: method call
* secondary:
** calling method's ``++@Transactional++`` annotation or, if none, calling method signature.
** called method's ``++@Transactional++`` annotation or signature
** message: "Incompatible method definition."
2021-09-20 15:38:42 +02:00
2021-06-08 15:52:13 +02:00
'''
2021-06-02 20:44:38 +02:00
== Comments And Links
(visible only on this page)
2023-05-25 14:18:12 +02:00
=== on 21 Nov 2014, 12:29:50 Freddy Mallet wrote:
I would tag this rule with the label "spring"
=== on 20 Jun 2018, 14:39:00 Alban Auzeill wrote:
@Ann I don't understand why there's a difference between ``++@Transactional++`` and ``++@Transactional(propagation = Propagation.REQUIRED)++`` (the default) ?
And I disagree with incompatible propagations list, this is my proposal:
||From||To||
| non-``++@Transactional++`` | MANDATORY, REQUIRED, REQUIRES_NEW |
| MANDATORY | NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW |
| NESTED | MANDATORY, NESTED, NEVER, NOT_SUPPORTED, REQUIRED, REQUIRES_NEW |
| NEVER | MANDATORY, REQUIRED, REQUIRES_NEW |
| NOT_SUPPORTED | MANDATORY, REQUIRED, REQUIRES_NEW |
| REQUIRED or ``++@Transactional++`` | NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW |
| REQUIRES_NEW | NESTED, NEVER, NOT_SUPPORTED REQUIRES_NEW |
| SUPPORTS | MANDATORY, NESTED, NEVER, NOT_SUPPORTED, REQUIRED, REQUIRES_NEW |
javadoc https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html[org.springframework.transaction.annotation.Propagation]
javadoc https://docs.oracle.com/javaee/7/api/javax/transaction/Transactional.html[javax.transaction.Transactional]
2021-06-03 09:05:38 +02:00
endif::env-github,rspecator-view[]