I heard that wechat search “Java fish” will change strong!

This article is in Java Server, which contains my complete series of Java articles, can be read for study or interview

(1) Preface

A while back in an interview I was asked if the @Transactional annotation can be used on private methods. If you were asked this question right now, what would your answer be?

The answer is that adding the @Transactional annotation to a private method doesn’t work. It’s not something you would normally use in a project, so I wrote this article to take a closer look at how Transactional can be used.

(2) several important parameters

Transactional Transactional means that a modified method runs as a transaction. Transactional means that all persistent operations in a method either take place or none of them. For example, order generation and inventory reduction need to be transactional operations in the order system.

Transactional is easy to use by adding annotations to methods where you need to add transactions:

@Transactional(rollbackFor = Exception.class)
public int createOrder(String orderId) {
    // Increase the order
    orderMapper.createOrder(orderId);
    // Deduct the account amount
    accountMapper.updateAccount(10);
    return 1;
}
Copy the code

The Transactional argument is important:

2.1 Isolation Level Isolation

There are three types of problems you might encounter in a database:

Dirty read: a transaction reads uncommitted update data from another transaction

Non-repeatable read: Data is read repeatedly and inconsistent

Phantom read: multiple reads with inconsistent data amount

Spring’s isolation level:

  1. ISOLATION_DEFAULT: this is a PlatfromTransactionManager default isolation level, using the database to the default transaction isolation level.

  2. ISOLATION_READ_UNCOMMITTED: Read is not committed. This is the lowest isolation level for a transaction, resulting in dirty reads, non-repeatable reads, and phantom reads.

  3. ISOLATION_READ_COMMITTED: Read committed, ORACLE default isolation level, phantom read and unrepeatable read risks.

  4. ISOLATION_REPEATABLE_READ: Repeatable reads, addressing the isolation level of non-repeatable reads, but still with phantom read risk.

  5. ISOLATION_SERIALIZABLE: The highest transaction isolation level, regardless of the number of transactions, can be executed after all the children of a transaction in another transaction, solve the dirty read, unrepeatable read and phantom read.

Higher isolation levels are not always better; higher isolation levels mean greater resource consumption, so trade-offs need to be made.

2.2 Propagation of Propagation attributes

Transaction propagation properties exist only in Spring; there is no such thing as propagation properties in database transactions

Spring has seven propagation properties:

PROPAGATION_REQUIRED– Supports the current transaction, or creates a new one if there are none. (the default)

PROPAGATION_SUPPORTS– Supports the current transaction, and executes non-transactionally if there is no transaction currently.

PROPAGATION_MANDATORY– The current transaction is supported, and an exception is thrown if there is no transaction currently.

PROPAGATION_REQUIRES_NEW– Creates a transaction, and suspends the current transaction if one exists.

PROPAGATION_NOT_SUPPORTED– Executes an operation in a non-transactional manner, and suspends the current transaction if one exists.

PROPAGATION_NEVER– Executes non-transactionally, throws an exception if a transaction currently exists.

PROPAGATION_NESTED– Executes within a nested transaction if one currently exists. If there are no transactions currently, an operation similar to PROPAGATION_REQUIRED is performed.

2.2.1 Interpretation of important propagation attributes:

If you have servicea.methoda (), call serviceb.methodb () inside it.

Spring uses the PROPAGATION_REQUIRED attribute by default. When Servicea.methoda () runs, a transaction is started, and the Serviceb.methodb () method finds that a transaction already exists, The transaction will no longer be started, so any method that reports an error will be called back.

PROPAGATION_REQUIRES_NEW Creates a transaction, and suspends the current transaction if one exists. When servicea.methoda () runs, start A transaction A. When running serviceb.methodb (), suspend transaction A and start transaction B. Even if transaction A is rolled back, transaction B still commits normally. To sum up: External transactions do not affect the commit and rollback of internal transactions.

PROPAGATION_NESTED Executes within a nested transaction if a transaction currently exists. The first thing you need to understand about nesting is the concept of checkpoints: when a checkpoint is set in a transaction, if a rollback occurs, it is rolled back to where the checkpoint was set, not the entire transaction. Nested transactions use the checkpoint Savepoint. When servicea.methoda () runs, start A transaction A. When serviceb.methodb () is run, a subtransaction B is opened, which takes a savepoint. If this transaction B fails, it will be rolled back to this savepoint without affecting the entire transaction. The bottom line is that internal transactions do not affect the commit and rollback of external transactions.

2.3 readOnly

The default is false. If set to true, the method is readable and an exception is thrown if data is modified.

2.4 rollbackForClassName/rollbackFor

Used to indicate which exception class or exception class name the rollback condition is.

2.5 norollbackForClassName/noRollbackFor

Used to specify which exception class or exception class name is the condition for not rolling back.

2.6 the timeout

This parameter is used to set the duration of transaction processing to prevent blocking of long events from occupying system resources.

2.7 the value

Specify the Spring transaction manager to use.

When does Transactional fail

3.1 When modifying non-public methods

The Transactional annotation does not work when you use Transactional to modify a non-public method:

3.2 Calling transaction methods inside a class

Transactional also does not work if a Transactional method is called inside a class.

@Service
public class OrderServiceImpl implements OrderService {
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int createOrder(String orderId) {
        // Increase the order
        orderMapper.createOrder(orderId);
        // Deduct the account amount
        accountMapper.updateAccount(10);
        return 1;
    }
    
    public void innerTransfer(a){
        createOrder("111"); }}Copy the code

3.3 No exception is thrown after an exception is caught

The Transactional annotation is disabled by default only after a RuntimeException is caught. If an exception is caught in your code and not thrown, the Transactional annotation fails:

@Transactional(rollbackFor = Exception.class)
public int createOrder(String orderId) {
    // Increase the order
    orderMapper.createOrder(orderId);
    // Deduct the account amount
    accountMapper.updateAccount(10);
    try {
        int i=1/0;
    }catch (RuntimeException e){
        e.printStackTrace();
    }
    return 1;
}
Copy the code

One solution is to throw another exception after the catch:

@Transactional(rollbackFor = Exception.class)
public int createOrder(String orderId) {
    // Increase the order
    orderMapper.createOrder(orderId);
    // Deduct the account amount
    accountMapper.updateAccount(10);
    try {
        int i=1/0;
    }catch (RuntimeException e){
        e.printStackTrace();
        throw new RuntimeException();
    }
    return 1;
}
Copy the code

(4) Summary

The Transactional annotation needs to focus on two parameters: Isolation, Propagation, and three failure cases. In addition, if there is a data source switch and the Transactional singleton fails, distributed transactions such as SeATA are needed.