[Translation] Spring in @Transcational annotation transaction isolation level behavior and communication

Original access

Foreword

In this tutorial, we will learn @Transactional annotation transaction isolation levels and the spread of behavior.

What @Transcational annotations are?

We can use the @Transactionaladd transaction support to a method comment.

We can set the transaction specified by its communication behavior, and rollback timeout condition. In addition, we can also specify a transaction manager through it.

Spring to achieve the creation of a transaction by creating a proxy object or bytecode manipulation, submission and rollback.

In the case of implementing transaction management through a proxy mode for calls from within the class @Transactionalwill fail.

Simply put, if we have a call callMethodmethod and the method is @Transactionalannotated marked.

Spring will use some of the code for this transaction management method for packaging, @Transactionalannotations can be achieved using the following pseudo-code says:

// 如有必要则创建事务
createTransactionIfNecessary();
try {
    // 调用callMethod方法
    callMethod();
    // 调用成功则提交事务
    commitTransactionAfterReturning();
} catch (exception) {
    // 调用失败则回滚事务
    completeTransactionAfterThrowing();
    throw exception;
}
复制代码

How to use @Transcational comment

We can use this annotation interfaces, classes, methods above, located in the interface, based on the method of @Transactionalpriority annotations overrides occur in different priority, priority bottom will be higher overwritten, covering from low to high They are:

  • interface
  • Superclass
  • class
  • Interface Method
  • Superclass method
  • Class Methods

Spring put a mark on the class @Transactionalnotes apply to all class publicon the way, so we do not need a separate method for tagging on @Transactional.

However, if we are in privateor protectedmarked @Transcational comment on the method, then Spring will ignore these notes and do not give any error.

Let's start marked @Transcational comment on the interface:

@Transactional
public interface TransferService {
    void transfer(String user1, String user2, double val);
}
复制代码

Under normal circumstances, it is not recommended label on the interface @Transactionalnotes, however, it is acceptable to use on certain interfaces, such as: Spring Data's @Repositoryinterface.

We can comment marked on the definition of the class, so you can cover the interface or superclass marked @Transactionalnotes:

@Service
@Transactional
public class TransferServiceImpl implements TransferService {
    @Override
    public void transfer(String user1, String user2, double val) {
        // ...
    }
}
复制代码

Now, let us mark this comment directly on the definition of the method:

@Transactional
public void transfer(String user1, String user2, double val) {
    // ...
}
复制代码

Transaction propagation behavior

Transaction propagation behavior defines transaction boundaries of our business logic, Spring start or interrupt a transaction based on the transaction propagation behavior of our configuration.

Spring by calling TransactionManager::getTransactionand then to get the method according to the propagation of the transaction or to create a transaction, it supports part in TransactionManagerthe propagation of affairs defined, but there are some special transaction propagation behavior needs through special TransactionManagerto support implementation.

REQUIRED

REQUIREDIt is the default transaction propagation behavior. For the purposes of propagation behavior, Spring checks whether there is an active transaction in the context of the current thread.

If there is no active transaction a new transaction is created.

If there is an active transaction will be accounted for in the current business logic of this transaction active range:

@Transactional(propagation = Propagation.REQUIRED)
public void requiredExample(String user) { 
    // ... 
}
复制代码

Because REQUIREDthe default transaction propagation behavior, so the above code can be abbreviated as:

@Transactional
public void requiredExample(String user) { 
    // ... 
}
复制代码

We look to create a REQUIREDpseudo-code propagation behavior matters:

 // 检测是否已经位于事务中
 if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    // 返回已有的事务
    return existing;
}
// 否则不使用事务
return createNewTransaction();
复制代码

SUPPORTS

For SUPPORTSpurposes, Spring will first check whether there is an active transaction thread context, if there is an active transaction using it. If not, do not use the transaction places a way to execute supportsExample()method ::

@Transactional(propagation = Propagation.SUPPORTS)
public void supportsExample(String user) { 
    // ... 
}
复制代码

Let's look at using SUPPORTSpseudo-code to create transaction propagation behavior:

// 检测是否已经位于事务中
if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    // 返回已有的事务
    return existing;
}
// 否则不使用事务
return emptyTransaction;
复制代码

MANDATORY

When the propagation behavior is set MANDATORY, if Spring did not find an active transaction in the context of the thread will throw an exception:

@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryExample(String user) { 
    // ... 
}
复制代码

Let's look at pseudo-code representation of the corresponding option:

// 检测是否已经位于事务中
if (isExistingTransaction()) 
{
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    // 返回已有的事务
    return existing;
}
// 否则抛出异常
throw IllegalTransactionStateException;
复制代码

NEVER

When the propagation behavior is set NEVERwhen, Spring found in the context of the thread will throw an exception when an active transaction:

@Transactional(propagation = Propagation.NEVER)
public void neverExample(String user) { 
    // ... 
}
复制代码

Let's look at using NEVERpseudo-code to create transaction propagation behavior:

// 检测是否已经位于事务中
if (isExistingTransaction()) {
    // 如果位于事务中就抛出异常
    throw IllegalTransactionStateException;
}
// 否则不使用事务
return emptyTransaction;
复制代码

NOT_SUPPORTED

When the propagation behavior is set to NOT_SUPPORTEDthe time, Spring will thread context of existing transactions pending, then in a manner not using transactions to execute notSupportedExample()method:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedExample(String user) { 
    // ... 
}
复制代码

JTATransactionManagerTransaction Manager in a manner out of the box support true transaction suspension. Other transaction manager is stored by conventional transaction REFERENCE then cleared from the thread context of the method to simulate the pending transaction.

REQUIRES_NEW

When the propagation behavior is set REQUIRES_NEWwhen, Spring found an active transaction thread context, it is first suspended and then create a new transaction to execute requiresNewExample()method:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewExample(String user) { 
    // ... 
}
复制代码

With NOT_SUPPORTEDoptions similar to the real transaction suspension, we need to use JTATransactionManagerthe transaction manager

This option corresponds to the pseudo code implementation:


// 检测是否已经位于事务中
if (isExistingTransaction()) {
    // 先挂起已存在的事务
    suspend(existing);
    try {
        // 然后创建一个新事务
        return createNewTransaction();
    } catch (exception) {
        // 如果新事务发生异常则恢复旧事务
        resumeAfterBeginException();
        throw exception;
    }
}
// 没有找到旧事务,直接创建新事务
return createNewTransaction();
复制代码

NESTED

For the NESTEDpurposes of whether there is an active transaction Spring detect thread context.

If there is already saved a point is established on the transaction, which means that if our nestedExample()method exception occurs, the transaction will be rolled back to save at the point we have established.

If there is no active transaction is detected, then the behavior of the options and REQUIREDthe options the same.

DataSourceTransactionManagerSupport way out of the box with this option. In addition, JTATransactionManagersome implementations also support this option.

JpaTransactionManagerOnly JDBC connection support NESTEDoptions. However, if we nestedTransactionAllowedflag is set true, and we JDBC driver also supports saving point function, it also applies to the JPA transaction JDBCaccess code.

Finally, let us set the propagation behavior NESTED:

@Transactional(propagation = Propagation.NESTED)
public void nestedExample(String user) { 
    // ... 
}
复制代码

Transaction isolation level

ACID property is isolation of a:

  • Atomicity (Atomicity)
  • Consistency (Consistency)
  • Isolation (Isolation)
  • Persistent (Durability)

Isolation is represented among concurrent transaction visibility data.

Each level of isolation can prevent concurrent transactions zero or more concurrent side effects occur:

  • Dirty read: read to change the concurrent transaction has not been submitted.
  • Non-repeatable read: In a concurrent transaction, the transaction data after a change to a row, another transaction the bank's multiple reads might get different results.
  • Magic Reading: In a concurrent transaction, if a transaction is added to or deleted from the line and committed, other transactions may get different results in the re-run range queries.

We can @Transactional::isolationset the transaction isolation level. In the Spring, it can use the following five enumeration values:

  • DEFAULT
  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

Spring isolation level management

In the Spring of default transaction isolation level DEFAULT. So, when you create a new Spring transaction isolation level is set in our database isolation level. Therefore, if we need to pay attention to the data in the database to modify it.

We have to think in the call chain, when each method corresponds to a different transaction isolation level what happens.

Under normal circumstances, the transaction will be set when you create a transaction isolation level, if we do not want our method call chain there are different transaction isolation level, we can set TransactionManager::setValidateExistingTransactionas trueits function using pseudo-code is expressed as:

// 如果隔离级别不是DEFAULT
if (isolationLevel != ISOLATION_DEFAULT) {
    // 当前事务设置的隔离级别与已存在事务的隔离级别不一致则抛出异常
    if (currentTransactionIsolationLevel() != isolationLevel) {
        throw IllegalTransactionStateException
    }
}
复制代码

Now let us understand the different levels of isolation and its role.

READ_UNCOMMITTED

READ_UNCOMMITTEDIs the lowest level of isolation, with the highest performance concurrent access, this level of dirty read happens, the case can not be repeated phantom read and read.

We can set this as a method or class isolation level:

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void log(String message) {
    // ...
}
复制代码

Postgres does not support the READ_UNCOMMITTEDisolation level, it uses the READ_COMMITEDisolation level instead. Oracle does not support READ_UNCOMMITTEDlevel.

READ_COMMITTED

The second level of isolation is READ_COMMITTED. It prevents dirty reads to occur.

Or the presence of non-repeatable read and phantom reads under this isolation level, so the change has no effect on us in concurrent transactions uncommitted, however, the transaction is committed to re-query the same data to different results may occur.

The following code sets the transaction isolation level READ_COMMITTED:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void log(String message){
    // ...
}
复制代码

REPEATABLE_READ

The third level of isolation is REPEATABLE_READ. It is possible to prevent dirty reads, unrepeatable reads occurring. Therefore, we are not affected by the change of concurrent uncommitted transaction.

Similarly, when we re-query row of data, we will not get a different result. But in the re-run range queries, we may get a new line to add or delete.

In addition, it is satisfying to prevent lost updates the minimum level required, when two or more concurrent transactions to read and update the same row, lost updates can occur. REPEATABLE_READNot allow concurrent transactions to access the same data row. Therefore, the situation will not be lost updates.

The following code sets the transaction isolation level READ_COMMITTED:

@Transactional(isolation = Isolation.REPEATABLE_READ) 
public void log(String message){
    // ...
}
复制代码

REPEATABLE_READIs the default isolation level in Mysql. Oracle does not support the REPEATABLE_READisolation level.

SERIALIZABLE

SERIALIZABLEThe highest level is the isolation level. It is possible to prevent dirty reads, non-repeatable read and phantom read.

Because it becomes a serial concurrent transactions, so it may result in the lowest performance concurrent access,

In other words, they have the same result of parallel execution of a group serial execution and results of the transaction.

Now look at how the isolation level to SERIALIZABLE:

@Transactional(isolation = Isolation.SERIALIZABLE)
public void log(String message){
    // ...
}
复制代码

to sum up

In this tutorial, we discussed in detail the spread of behavior @Transaction. Then we learn the side effects associated with isolation level of concurrent transactions.

As always, you can GitHub find the complete code.

Guess you like

Origin juejin.im/post/5e3d1003f265da573c0c6853