I often use this annotation in my daily work, but I only use it briefly. I don’t know why. This article is therefore a summary of the @Transactional annotation.

1. Spring Transaction Management

Spring supports both programmatic and declarative transaction management.

Programmatic transaction management is the use of code to manually commit and roll back transactions.

Declarative transaction management is based on AOP, which essentially intercepts methods before and after, then creates or joins a transaction before the target method begins, and commits or rolls back the transaction based on the execution of the target method.

The @Transactional annotation is a common implementation of declarative transaction management.

The @Transactional annotation attribute

Propagation

Propagation represents the propagation behavior of transactions, that is, how to manage transactions with multiple nested transactions. There are six types of transmission:

  • Propagation.REQUIRED:Is the default value for propagation behaviorIf a transaction currently exists, join the transaction. If no transaction currently exists, create a new transaction.
  • Propagation.SUPPORTS: If a transaction exists, join the transaction. If no transaction currently exists, it continues in a non-transactional manner. * (What is the difference between using @transactional (Propagation = Propagation.SUPPORTS) and not using @transactional?)
  • Propagation.MANDATORY: If a transaction exists, join the transaction. If no transaction currently exists, an exception is thrown.
  • Propagation.REQUIRES_NEW: Recreates a new transaction and suspends it until a new transaction is committed, if one currently exists. (Spring transaction Propagation mechanism)
  • Propagation.NOT_SUPPORTED: Runs in a non-transactional manner, suspending the current transaction if one exists.
  • Propagation.NEVER: Runs in a non-transactional manner, throwing an exception if a transaction currently exists.
  • Propagation.NESTEDPropagation.

isolation

The isolation boundary of a transaction refers to the degree of isolation between several concurrent transactions, including the following:

  • Isolation.DEFAULT:The default valueTo use the database default isolation level.(mysql repeatable; Essentially a readcommit)
  • Isolation.READ_UNCOMMITTED: Read uncommitted, indicating that a transaction can read data modified by other transactions but not yet committed. This isolation level mayCause dirty read.
  • Isolation.READ_COMMITTED:Reading has been submitted, indicating that a transaction can only read data already committed by another transactionTo prevent dirty read, butPhantom and unrepeatable reads may occur.
  • Isolation.REPEATABLE_READ:Repeatable readThe isolation level indicates that a transaction can execute a query multiple times throughout the process and return the same records each time.This isolation level protects against dirty and unrepeatable reads, but phantom reads may occur.
  • Isolation.SERIALIZABLE: the highest isolation sector. All transactions are executed sequentially to prevent dirty reads, unrepeatable reads, and phantom reads. But the performance is also the worst.
Isolation level Dirty read Unrepeatable read The phantom read
READ-UNCOMMITTED Square root Square root Square root
READ-COMMITTED x Square root Square root
REPEATABLE-READ x x Square root
SERIALIZABLE x x x

Here is a brief introduction to several isolation levels of the isolation attribute. For more details on isolation levels, see other articles.

timeout

Timeout period for a transaction. Default value is -1. If the time limit is exceeded but the transaction has not completed, the transaction is automatically rolled back.

readOnly

Specifies whether the transaction is read-only. The default value is false. To ignore methods that do not require transactions, such as reading data, you can set read-only to true.

rollbackFor

Used to specify the type of exception that can trigger transaction rollback. Multiple exception types can be specified.

By default, only non-checked exceptions (RunTimeException, Error, and so on) are rolled back; checked exceptions (including SQLException) are not rolled back.

noRollbackFor

Specify an exception type that does not trigger a transaction rollback, as opposed to the rollbackFor property.

Transactional annotation source (Spring 5.2.8) :

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value(a) default "";

    @AliasFor("value")
    String transactionManager(a) default "";

    Propagation propagation(a) default Propagation.REQUIRED;

    Isolation isolation(a) default Isolation.DEFAULT;

    int timeout(a) default- 1;

    boolean readOnly(a) default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}
Copy the code

2. Precautions for use

Transactional can work on interfaces, interface methods, classes, and class methods. When used on a class, all public methods of several classes will have transaction attributes of that type, and we can also use this annotation at the method level to override class-level definitions.

Transactional @Transactional is easy to use, but there are a few things that make @Transactional Transactional fail.

2.1 Several common failure situations

Transactional applies to methods that are not public ornamented

Reason: When Spring manages transactions through an AOP proxy, it checks if the method is a public modifier. Otherwise, it does not get the @Transactional attribute configuration information.

When a Nan-Transactional method calls a Transactional method in the same class, the transaction fails

The reason: Spring uses dynamic proxy (AOP) to manage transactions. When a scan is initialized, a proxy object is dynamically generated for a Transactional annotated class. When a Transactional method is called, it is actually through the proxy class, but only between proxy objects. Can trigger aspect logic.

In the same class, method B calls method A, calling the methods of the original object instead of the proxy object. So Spring cannot cut through this call, and thus cannot ensure transactionality through annotations.

Ps: In the same class that calls methods with the Transactional annotation, transactions can be Transactional if called methods do not have the Transactional annotation

public void doTheThing(a) {   
 / /... actuallyDoTheThing(); // Noncompliant
 }
 @Transactional 
 public void actuallyDoTheThing(a) { 
 / /...
 }  
Copy the code

Solutions:

  • 1, in the same class, call method belt@Transactionalannotations
  • 2. Call through proxy objects

In a try catch block, the exception is not thrown again

A common failure scenario is where the exception is caught and not thrown to the slice layer.

@Transactional
public void doSomething(){
    try{
        //
    }catch(exception){
       // No exception is thrown again}}Copy the code

The rollbackFor property is incorrectly set

The @Transactional annotation by default rolls back only unchecked exceptions. Exceptions such as SqlException are checked exceptions. If not specified in the rollbackFor attribute, transactions will not rollback when a checked exception is thrown.

reference

  • Rattle off six failure scenarios for the @Transactional annotation
  • The @Transactional annotation of Spring is used in detail
  • Analyze why the Spring transaction @Transactional annotation does not work when called between methods in the same class and the solution