Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How does Spring implement the propagation feature for transactions

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "how Spring realizes the propagation characteristics of transactions". In the daily operation, I believe that many people have doubts about how Spring realizes the propagation characteristics of transactions. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the doubts of "how Spring realizes the propagation characteristics of transactions". Next, please follow the editor to study!

Business

What is the business?

It is a first-order atomic SQL operation, an independent unit of work. If the SQL statement in the unit of work is executed successfully, it will all succeed, and one failure will all be rolled back.

Known to the public at the same time as database transactions is ACID. That is, the atomicity of the database (Atomicity), consistency (Consistency), isolation (Isolation) and persistence (Durability). Just like the application for mutual exclusion between threads, thread safety, etc., need to do a lot of work, the database for ACID, also do a lot of work.

Isolation level

Four isolation levels are defined in the SQL standard, specifying which are visible between transactions and which are visible within transactions.

READ UNCOMMITTED (uncommitted read)-> between two transactions, one transaction has not yet been committed, but the other has seen it.

READ COMMITTED (commit read)-> between two transactions, only after one transaction commits, the other transaction can be seen.

REPEATABLE READ (repeatable readable)-- > in the execution of a transaction, the result read many times is the same, even if other transactions have been modified, they are "invisible" first.

SERIALIZABLE (serializable)-> the highest isolation level that forces transactions to execute serially.

Because these isolation levels will cause so-called "dirty reading", "illusory reading", "non-repeatable reading" and other problems, it is necessary to choose the applicable one according to the specific scenario.

How to set the isolation level

For the isolation level of MySQL, you can either set it globally or set the Session level.

The syntax of the official document settings is as follows:

When we connect to MySQL Server through the client, we can make some configuration, as you can see through the URL of jdbc, so the setting is at the Session level.

Referring to the official documentation above, the command to set the session isolation level is it:

Set session transaction isolation level SERIALIZABLE

Note that the default isolation level for MySQL is REPEATABLE READ, my correction. At the same time, the SQL of the current isolation level is queried here. The old version is SELECT @ @ TX_ISOLATION;, and the new version is SELECT @ @ Transaction_ISOLATION;.

What about the Spring transaction?

After looking at the isolation level of the database, let's look at the implementation in Spring.

In Spring, there are five definitions of isolation levels, four of which are consistent with the database, and the other includes a DEFAULT, which represents a non-specific setting and is directly defined by the database.

We see that the isolation level of the database can be set to either the GLOBAL level or the SESSION level, and each SESSION represents a connection. In Spring, we operate the database and complete CRUD through independent "connections". At this point, we should know that the setting at the Spring level is set through session-level isolation, which corresponds to the transaction isolation level in the database.

What about the characteristics of communication? Since the database does not directly support such features, Spring is implemented in a roundabout way according to the requirements of different propagation characteristics.

To sum up, for cascading operations, in the case of REQUIRED_NEW, the so-called suspending the current transaction and opening a new transaction is that Spring applies for a new Connection, so that it corresponds to a new transaction, and then binds the connection to the current thread before continuing to execute

For nested transactions, the underlying layer implements the so-called "sub-transactions" through the SAVEPOINT of the database.

If you are familiar with the Frame of each method in the code DEBUG process, you can compare that if you perform a failed rollback, you can specify a rollback to a SAVEPOINT of the current transaction, without the need for a full rollback.

At the Spring level, this is done through JDBC 3.0. Take a look at the code comment below.

*

This transaction manager supports nested transactions via the JDBC 3.0 * {@ link java.sql.Savepoint} mechanism. The * {@ link # setNestedTransactionAllowed "nestedTransactionAllowed"} flag defaults * to "true", since nested transactions will work without restrictions on JDBC * drivers that support savepoints (such as the Oracle JDBC driver).

Part of the code for transaction suspension is as follows:

/ * Suspend the given transaction. Suspends transaction synchronization first, * then delegates to the {@ code doSuspend} template method. * @ param transaction the current transaction object * (or {@ code null} to just suspend active synchronizations, if any) * @ return an object that holds suspended resources * (or {@ code null} if neither transaction nor synchronization active) * @ see # doSuspend * @ see # resume * / @ Nullable protected final SuspendedResourcesHolder suspend (@ Nullable Object transaction) throws TransactionException {if (TransactionSynchronizationManager.isSynchronizationActive ()) {List suspendedSynchronizations = doSuspendSynchronization () Try {Object suspendedResources = null; if (transaction! = null) {suspendedResources = doSuspend (transaction);} String name = TransactionSynchronizationManager.getCurrentTransactionName (); TransactionSynchronizationManager.setCurrentTransactionName (null); boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly (); TransactionSynchronizationManager.setCurrentTransactionReadOnly (false); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel (); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel (null); boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive () TransactionSynchronizationManager.setActualTransactionActive (false); return new SuspendedResourcesHolder (suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);}}

The logic is in doSuspend. Keep looking.

Override protected Object doSuspend (Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; txObject.setConnectionHolder (null); return TransactionSynchronizationManager.unbindResource (obtainDataSource ());} @ Override protected void doResume (@ Nullable Object transaction, Object suspendedResources) {TransactionSynchronizationManager.bindResource (obtainDataSource (), suspendedResources);}

Then the corresponding bind and unbind operations are implemented by binding or removing the resource object from the resources corresponding to the current thread in the ThreadLocal object.

Private static final ThreadLocal resources = new NamedThreadLocal ("Transactional resources"); / * * Bind the given resource for the given key to the current thread. * @ param key the key to bind the value to (usually the resource factory) * @ param value the value to bind (usually the active resource object) * @ throws IllegalStateException if there is already a value bound to the thread * @ see ResourceTransactionManager#getResourceFactory () * / public static void bindResource (Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary (key); Assert.notNull (value, "Value must not be null"); Map map = resources.get () / / set ThreadLocal Map if none found if (map = = null) {map = new HashMap (); resources.set (map);} Object oldValue = map.put (actualKey, value); / / Transparently suppress a ResourceHolder that was marked as void... If (oldValue instanceof ResourceHolder & ((ResourceHolder) oldValue) .isVoid () {oldValue = null;}}

Comparing the instructions and code above, the difference between the two propagation features is also clear:

The characteristics of NESTED are essentially different save points of the same transaction. If the outer transaction rollback is involved, the inner transaction will also be rolled back.

The implementation of REQUIRED_NEW corresponds to a new transaction and gets the new resource, so when the outer transaction is rolled back, the inner transaction is not affected.

At this point, the study of "how Spring implements the propagation characteristics of transactions" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report