"God's perspective" illustrates the principle of the propagation mechanism of Spring transactions

The "hands" of


database transactions The transaction function of the database has been implemented by the database itself. It leaves the user with three instructions: open the transaction, commit the transaction and roll back the transaction.

Start transaction is generally start transaction or begin transaction. Commit transactions are usually commit. Rollback transactions are usually rollback.

Databases usually have switches for automatically opening and committing transactions. After opening, the transaction will be automatically opened and committed or rolled back. In this way, the user has no perception.


JDBC transaction "hands"


JDBC implements the protocol for accessing the database, which can be considered as an encapsulation of some instructions, of course, including these transaction instructions. They are all done in the java.sql.Connection interface.

It represents a connection to the database. When the connection is established, the default transaction is already opened, and the transaction is automatically committed after executing a SQL statement by default.

It can be detected through the following API:

boolean getAutoCommit();

Normally, we don’t want the transaction to be committed one by one, but to execute several SQL statements and commit it at once. At this time, we need to change this automatic commit the behavior of.

It can be set through the following API:

void setAutoCommit(boolean autoCommit);

When we disable autocommit, we must remember to commit the transaction manually, otherwise the result can be imagined. Of course, remember to manually roll back when you need to roll back.

You can submit the transaction through the following API:

void commit();

You can roll back the transaction through the following API:

void rollback();

In this way, we can control the behavior of the transaction at the Java code level. At the same time, we should also realize that transactions at the Java code level are tied to the instance object of Connecton.

In other words, only SQL statements executed on the same Connection object will be in the same transaction. SQL statements executed on different Connection objects will never be in the same transaction.

More precisely, the latter constitutes a distributed transaction, and the former is usually called a local transaction. Of course, distributed transactions have their own solutions, but local transactions can no longer be used.

Remarks : The above are actually basic common sense, but the current ORM framework is too good, so many young coders have no chance to contact these .


Spring's transaction "hands"


Spring implements declarative transactions through the use of @Transactional annotations, and can affect the propagation characteristics of transactions by setting the Propagation attribute.

The propagation characteristic of the transaction actually means that when several methods of the Service layer are intertwined with each other, which method code is executed in the same transaction, and which method code is not.

From the previous analysis, it can be seen that the method code executed in the same transaction must use the same Connection object. When the transaction is switched, the Connection object behind it must be switched to the corresponding one.

Therefore, when the execution process enters/exits different methods, Spring will switch the Connection object behind the corresponding according to the propagation characteristics of the annotations on the method, including creating a new Connection object, submitting or rolling back a transaction, etc.

We know that when writing the Service layer method or Mapper layer method, the Connection object is not accessible at all, so it is even more impossible to pass it back and forth in the form of parameters blatantly, and can only be operated in the dark.

Since these intertwined method codes eventually run in the same thread, it is most suitable to use the ThreadLocal of the thread to implement the operation behind it.

Just create/switch the Connection object at the entry/exit of the method call, and submit/roll back the transaction on the Connection object.


The principle of the propagation characteristics of


Spring transactions Spring transactions are implemented through agents, usually through CGLIB manipulation bytecode to generate subclasses, because these codes for opening/committing transactions must be dynamically added.

The following is an example to illustrate that there are four methods and their corresponding propagation characteristics:

Method one, the propagation characteristic is REQUIRED:

void method1();

Method two, the propagation characteristic is REQUIRED:

void method2();

Method three, the propagation characteristic is REQUIRES_NEW:

void method3();

Method four, the propagation characteristic is REQUIRED:

void method4();

Assuming that the calling relationship between them is that the method two, three and four are called sequentially in method one:

void method1() {     method2();     method3 ();     method4(); } can be represented by the following figure, figure 01:






 


Then after Spring generates a proxy, each method will be rewritten, and the transaction will be opened before each original method, and the transaction will be committed/rolled back after the method.

The equivalent pseudo code is as follows:

beginTx();
void method1() {     beginTx();     method2();     commit/rollbackTx();     beginTx();     method3();     commit/rollbackTx();     beginTx();     method4( );     commit/rollbackTx(); } commit/rollbackTx(); can be represented by the following figure, figure 02:













 


The red up arrow corresponds to the beginTx() operation, and the blue down arrow corresponds to the commit/rollbackTx() operation.

Let's take a look at the specific execution process:

1. Start the transaction of the execution method 1, Figure 03:
 


Since the first method requires a transaction, there is no transaction in the thread at this time, so a new transaction is opened, that is, a new Connection object is created and bound to the ThreadLocal of the thread.

2. The entry method is executed at the beginning, Figure 04:
 


3. Execute the open transaction of Method 2, Figure 05:
 


Since the second method requires a transaction, there is already a transaction in the thread at this time, so you can directly participate in/use this transaction.

4. Enter method two and start execution, Figure 06:
 


Fifth, method 2 is completed to execute the commit transaction, Figure 07:
 


Since the transaction is not newly created in method two, it is just participating, so it is not eligible to submit the transaction, so it does not actually submit the transaction.

6. After method two is over, return to method one to execute, Figure 08:
 


Seven, execute method three to open the transaction, Figure 09:
 


Because method three requires new things, a new transaction is created and the current existing transaction is suspended so that the current thread uses the new transaction.

That is, create a new Connection object, unbind the current existing Connection object from the ThreadLocal of the thread, and bind the new Connection object to the ThreadLocal of the thread.

What about the original Connection object? Of course, it is stored, and the form of storage is attached to the new Connection object, that is, associated with the new object.

8. Enter method three and start execution, Figure 10:
 


It can be seen that the original transaction is equivalent to temporarily stored in the new transaction. Note that this statement is only an image metaphor.

Nine, method three is completed to execute the commit transaction, Figure 11:
 


Since this transaction is a new transaction, that is, it was created by method three instead of participating, so it has the right to commit, so the transaction is really committed.

10. After method three, return to method one to execute, Figure 12:
 


The transaction of method 3 is completed, but the transaction of method 1 is temporarily stored in it, so it is restored to the thread.

11. Perform the opening transaction of Method 4, Figure 13:
 


Since method 4 requires a transaction, there is already a transaction in the thread at this time, so you can directly participate in/use this transaction.

12. Enter method 4 and start execution, Figure 14:
 


13. Method 4 is completed to execute the commit transaction, Figure 15:
 


Since the transaction is not newly created by method four, it is just participating, so it is not eligible to submit the transaction, so it does not actually submit the transaction.

14. After method four, return to method one for execution, Figure 16:
 


Fifteen, method one is completed to execute the commit transaction, Figure 17:
 


Since this transaction is a new transaction, that is, it is created by method one instead of participating, so it has the right to commit, so the transaction is really committed.

16. All transactions are completed and only one empty thread remains, Figure 18:
 


The transaction of method 1 is completed. Since there are no other transactions temporarily stored in it, there are no more transactions, so there is only one empty thread left.

Note : This is just the original understanding of the implementation of the propagation feature, so it is relatively simple, and the actual code implementation needs to consider many things, so it will be much more complicated.

 

Guess you like

Origin blog.csdn.net/weixin_45132238/article/details/108710750