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 @Transactional
add 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 @Transactional
will fail.
Simply put, if we have a call callMethod
method and the method is @Transactional
annotated marked.
Spring will use some of the code for this transaction management method for packaging, @Transactional
annotations 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 @Transactional
priority 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 @Transactional
notes apply to all class public
on the way, so we do not need a separate method for tagging on @Transactional
.
However, if we are in private
or protected
marked @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 @Transactional
notes, however, it is acceptable to use on certain interfaces, such as: Spring Data's @Repository
interface.
We can comment marked on the definition of the class, so you can cover the interface or superclass marked @Transactional
notes:
@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::getTransaction
and then to get the method according to the propagation of the transaction or to create a transaction, it supports part in TransactionManager
the propagation of affairs defined, but there are some special transaction propagation behavior needs through special TransactionManager
to support implementation.
REQUIRED
REQUIRED
It 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 REQUIRED
the default transaction propagation behavior, so the above code can be abbreviated as:
@Transactional
public void requiredExample(String user) {
// ...
}
复制代码
We look to create a REQUIRED
pseudo-code propagation behavior matters:
// 检测是否已经位于事务中
if (isExistingTransaction()) {
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
// 返回已有的事务
return existing;
}
// 否则不使用事务
return createNewTransaction();
复制代码
SUPPORTS
For SUPPORTS
purposes, 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 SUPPORTS
pseudo-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 NEVER
when, 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 NEVER
pseudo-code to create transaction propagation behavior:
// 检测是否已经位于事务中
if (isExistingTransaction()) {
// 如果位于事务中就抛出异常
throw IllegalTransactionStateException;
}
// 否则不使用事务
return emptyTransaction;
复制代码
NOT_SUPPORTED
When the propagation behavior is set to NOT_SUPPORTED
the 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) {
// ...
}
复制代码
JTATransactionManager
Transaction 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_NEW
when, 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_SUPPORTED
options similar to the real transaction suspension, we need to use JTATransactionManager
the 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 NESTED
purposes 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 REQUIRED
the options the same.
DataSourceTransactionManager
Support way out of the box with this option. In addition, JTATransactionManager
some implementations also support this option.
JpaTransactionManager
Only JDBC connection support NESTED
options. However, if we nestedTransactionAllowed
flag is set true
, and we JDBC driver also supports saving point function, it also applies to the JPA transaction JDBC
access 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::isolation
set 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::setValidateExistingTransaction
as true
its 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_UNCOMMITTED
Is 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_UNCOMMITTED
isolation level, it uses the READ_COMMITED
isolation level instead. Oracle does not support READ_UNCOMMITTED
level.
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_READ
Not 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_READ
Is the default isolation level in Mysql. Oracle does not support the REPEATABLE_READ
isolation level.
SERIALIZABLE
SERIALIZABLE
The 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.