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

This paper summarizes the use of transactions in Spring, abstract mechanism and simulated Spring transaction implementation.

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

This article introduces the relevant knowledge of "summarizing the use of transactions in Spring, abstract mechanisms and simulating the implementation of Spring transactions". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

The outline of this article is as follows:

Spring transaction application outline

Programmatic transaction

Spring provides two methods of programmatic transaction management

Use TransactionTemplate or TransactionalOperator.

Directly implement the TransactionManager interface

If you are using imperative programming, Spring recommends using TransactionTemplate for programmatic transaction management, and if you are responsive programming, TransactionalOperator is more appropriate.

TransactionTemplate

Use the example (the example provided by the official website I use directly here)

Public class SimpleService implements Service {private final TransactionTemplate transactionTemplate; / / initialize transactionTemplate using constructs / / A transactionManager public SimpleService (PlatformTransactionManager transactionManager) {this.transactionTemplate = new TransactionTemplate (transactionManager) is required } public Object someServiceMethod () {return transactionTemplate.execute (new TransactionCallback () {public Object doInTransaction (TransactionStatus status) {/ / implement your related business logic updateOperation1 (); return resultOfUpdateOperation2 ();}});}}

In the above example, we show that we use TransactionTemplate to complete the transaction management, by implementing the TransactionCallback interface and completing our business processing in its doInTransaction method. We can roughly take a look at the implementation of TransactionTemplate's execute method:

Public T execute (TransactionCallback action) throws TransactionException {Assert.state (this.transactionManager! = null, "No PlatformTransactionManager set"); if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {return ((CallbackPreferringPlatformTransactionManager) this.transactionManager) .execute (this, action);} else {/ / 1. Open the transaction through the transaction manager TransactionStatus status = this.transactionManager.getTransaction (this); T result; try {/ / 2. Execute the passed business logic result = action.doInTransaction (status);} catch (RuntimeException | Error ex) {/ / 3. Roll back rollbackOnException (status, ex); throw ex;} catch (Throwable ex) {/ / 3. If an exception occurs, roll back rollbackOnException (status, ex); throw new UndeclaredThrowableException (ex, "TransactionCallback threw undeclared checked exception");} / / 4. If normal execution is completed, commit transaction this.transactionManager.commit (status); return result;}}

We will not look at the specific implementation of these methods for the time being. They will be described in detail in the subsequent source code analysis. The reason why this code is posted is to give people a better understanding of how TransactionTemplate works: in fact, the business logic is encapsulated through a TransactionCallback, and then TransactionTemplate will be called in the context of the transaction.

In the above example, doInTransaction has a return value, but sometimes it is not necessary, in which case we can use TransactionCallbackWithoutResult to replace TransactionCallback.

TransactionTemplate.execute (new TransactionCallbackWithoutResult () {protected void doInTransactionWithoutResult (TransactionStatus status) {updateOperation1 (); updateOperation2 ();}})

❝we can actually specify transaction properties through TransactionTemplate, such as isolation level, timeout, propagation behavior, and so on.

TransactionTemplate is thread-safe, we can configure a TransactionTemplate globally, and then all classes share the TransactionTemplate. However, if a class requires a special transaction configuration, such as a custom isolation level, then it is necessary to create a different TransactionTemplate. ❞

TransactionOperator

❝TransactionOperator is suitable for responsive programming, so ❞will not be introduced in detail here.

TransactionManager

In fact, TransactionTemplate also uses TransactionManager to complete transaction management. We have seen the implementation of its execute method before. In fact, the internal method is called TransactionManager, which is actually divided into several steps.

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Open a transaction

Execute business logic

Rollback when an exception occurs

Commit the transaction on normal execution

Here, I will directly use the example given on the official website.

/ / define transaction DefaultTransactionDefinition def = new DefaultTransactionDefinition (); def.setName ("SomeTxName"); def.setPropagationBehavior (TransactionDefinition.PROPAGATION_REQUIRED); / / txManager, transaction manager / / start a transaction through the transaction manager TransactionStatus status = txManager.getTransaction (def); try {/ / complete its own business logic} catch (MyException ex) {/ / an exception occurs and roll back txManager.rollback (status) Throw ex;} / / normal execution completed, commit transaction txManager.commit (status)

In fact, what we focus on in the later source code analysis is the TransactionManager source code.

Declarative affairs

After we have some knowledge of programming transactions, we will find that there are several problems with programming transactions:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Our business code is intermingled with the transaction management code.

"every place that needs transaction management needs to write repetitive code."

How to solve the problem? This is about to use declarative transactions. There are generally two ways to realize declarative transactions.

XML-based configuration

Based on annotations

The implementation principle of declarative transaction is as follows (the picture is from the official website):

Realization principle

It is actually a combination of APO automatic proxy and transaction-related API. By turning on the AOP automatic proxy and registering the notification (Transaction Advisor) required by the transaction with the container, the transaction-related API is called in Transaction Advisor, but the TransactionManager method is also called internally.

Let's not talk about this way based on XML configuration. I haven't used XML configuration in the past two years, so let's mainly look at the implementation of declarative transactions through annotations. It mainly involves two core comments.

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

@ EnableTransactionManagement

@ Transactional

The @ EnableTransactionManagement annotation has two main functions: one is to enable the AOP automatic proxy, and the other is to add the Transaction Advisor needed to add transactions. If you know anything about AOP, you should know that an Advisor is actually an Advice bound to a Pointcut. The Advisor bound to the annotation through the @ EnableTransactionManagement annotation is defined through @ Transactional.

I have omitted the example of Shenming-style affairs here. I believe there are few people who can't use it.

Spring's abstraction of transactions

The key to Spring transaction abstraction is the concept of transaction strategy, which is defined through the TransactionManager interface. TransactionManager itself is just a tagged interface, which has two direct subinterfaces

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

ReactiveTransactionManager, which is mainly used in the responsive programming model, is not the focus of our discussion.

PlatformTransactionManager, we use this interface under the imperative programming model.

❝can write a separate article on both responsive and imperative programming. This article does not focus on these two programming models. It can be considered that we usually use imperative programming ❞.

PlatformTransactionManager

PlatformTransactionManager interface definition

Public interface PlatformTransactionManager extends TransactionManager {/ / Open transaction TransactionStatus getTransaction (TransactionDefinition definition) throws TransactionException; / / commit transaction void commit (TransactionStatus status) throws TransactionException; / / rollback transaction void rollback (TransactionStatus status) throws TransactionException;}

PlatformTransactionManager is the central interface of Spring transaction mechanism under the imperative programming model, which defines the three steps necessary to complete a transaction, that is to say, it defines the specification of transaction implementation.

Open a transaction

Commit transaction

Roll back the transaction

Generally speaking, we will not implement this interface directly, but by inheriting AbstractPlatformTransactionManager, which is an abstract class that is mainly used as a template for transaction management. This abstract class has implemented transaction propagation behavior and transaction-related synchronization management.

Looking back at the three methods defined in the interface, the first is the method that opens the transaction. From the point of view of the method signature, its function is to obtain an object of type TransactionStatus through a TransactionDefinition. In order to better understand the abstraction of transactions in Spring, we need to understand these two interfaces.

TransactionDefinition

The API is defined as follows:

Public interface TransactionDefinition {/ / defines the propagation mechanism of transactions in 7: int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6 / / 4 isolation levels.-1 means to use the default isolation level of the database / / for example, under MySQL, ISOLATION_REPEATABLE_READ (repeatable) int ISOLATION_DEFAULT =-1; int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2; int ISOLATION_REPEATABLE_READ = 4; int ISOLATION_SERIALIZABLE = 8 / / the timeout of the transaction, which is not limited by default to int TIMEOUT_DEFAULT =-1; / / provides the get method default int getPropagationBehavior () {return PROPAGATION_REQUIRED;} default int getIsolationLevel () {return ISOLATION_DEFAULT;} default int getTimeout () {return TIMEOUT_DEFAULT for the above three attributes } / / whether the transaction is read-only, the default is not default boolean isReadOnly () {return false;} / / the name of the transaction @ Nullable default String getName () {return null;} / / returns a read-only TransactionDefinition / / only provides getter methods for attributes, all of which are the default values of static TransactionDefinition withDefaults () {return StaticTransactionDefinition.INSTANCE defined in the interface. }}

We can also know from the name of this interface that it mainly completes the abstraction of transaction definitions, some of which are inherent at the database level, such as isolation level, read-only, timeout, and name. Some are given by Spring, such as the propagation mechanism of transactions. Seven transaction propagation mechanisms are defined in Spring.

TransactionDefinition.PROPAGATION_REQUIRED: if there is a transaction, join it; if there is no transaction, create a new transaction.

TransactionDefinition.PROPAGATION_REQUIRES_NEW: creates a new transaction and suspends the current transaction if it exists.

TransactionDefinition.PROPAGATION_SUPPORTS: if there is a transaction, join it; if there is no transaction, continue to run in a non-transactional manner.

TransactionDefinition.PROPAGATION_NOT_SUPPORTED: runs in a non-transactional manner, suspending the current transaction if there is a current transaction.

TransactionDefinition.PROPAGATION_NEVER: runs in a non-transactional manner, throwing an exception if a transaction currently exists.

TransactionDefinition.PROPAGATION_MANDATORY: if there is a transaction, join it; if there is no transaction, throw an exception.

TransactionDefinition.PROPAGATION_NESTED: if there is a transaction, a transaction is created to run as a nested transaction of the current transaction; if there is no transaction, this value is equivalent to TransactionDefinition.PROPAGATION_REQUIRED.

On the dissemination of transactions in the source code analysis of the article I will focus on, now we can make an impression.

When we use declarative transactions, we will use the annotation @ Transactional to declare that a method requires transaction management. The attributes of the transaction can be defined in @ Transactional, and these attributes will actually be encapsulated in a TransactionDefinition. Of course, the encapsulation is definitely not an interface used directly, but an implementation class RuleBasedTransactionAttribute of this interface. RuleBasedTransactionAttribute, the inheritance relationship of this class is as follows:

RuleBasedTransactionAttribute

DefaultTransactionDefinition, which implements TransactionDefinition and provides default values for properties defined in it

/ / the default propagation mechanism is required. If there is no transaction, create a new transaction / / join the current transaction private int propagationBehavior = PROPAGATION_REQUIRED; / / isolation level with the default isolation level of the database always private int isolationLevel = ISOLATION_DEFAULT; / / default to-1, and do not set the timeout private int timeout = TIMEOUT_DEFAULT / / private boolean readOnly = false that is not read-only by default

TransactionAttribute, extends ``DefaultTransactionDefinition` and adds two new transaction attributes

/ / specifies the name of the transaction manager used by the transaction String getQualifier (); / / specifies what kind of exception to roll back boolean rollbackOn (Throwable ex)

DefaultTransactionAttribute, which inherits DefaultTransactionDefinition and implements the TransactionAttribute interface, defines the default rollback exception

/ / rollback public boolean rollbackOn (Throwable ex) {return (ex instanceof RuntimeException | | ex instanceof Error);} after RuntimeException/Error is thrown

Attributes such as rollbackFor of RuleBasedTransactionAttribute,@Transactional annotations are encapsulated in this class, allowing programmers to define the rollback exception themselves. If no rollback exception is specified, the default is "throw RuntimeException/Error before rollback".

TransactionStatus

This API is mainly used to describe the status of Spring transactions, and its inheritance relationship is as follows:

TransactionStatus

TransactionExecution, which is also used to describe the state of a transaction, is an extension of TransactionStatus, and the following methods are defined internally

/ / determine whether the current transaction is a new transaction / / if it is not a new transaction, then you need to add boolean isNewTransaction () to the existing transaction; / / whether the transaction is marked as RollbackOnly / / if marked as RollbackOnly, which means that the transaction can only be rolled back void setRollbackOnly (); boolean isRollbackOnly () / / whether the transaction is completed, rollback or commit means that the transaction has completed boolean isCompleted ()

SavepointManager, defines the method of managing the save point (Savepoint). When the isolation level is NESTED, it is achieved by setting the rollback point. These methods are defined internally.

/ / create SavePoint Object createSavepoint () throws TransactionException; / / rollback to the specified SavePoint void rollbackToSavepoint (Object savepoint) throws TransactionException; / / remove rollback Point void releaseSavepoint (Object savepoint) throws TransactionException

TransactionStatus, which inherits the above interfaces, provides two additional methods

/ / used to determine whether the current transaction has set the save point boolean hasSavepoint (); / / this method overrides the method in the parent interface Flushable / / is mainly used to refresh the session / / in the case of Hibernate/jpa, it is the flush method void flush () that calls its session/entityManager.

❝summary: through the above analysis, we will find that the main function of TransactionDefinition is to give a definition of the transaction attribute, and then the transaction manager creates the transaction according to the definition. TransactionStatus is mainly used to describe the status of the created transaction ❞.

After some understanding of TransactionDefinition and TransactionStatus, let's go back to the PlatformTransactionManager interface itself. PlatformTransactionManager, as the basic interface of the transaction manager, only defines three methods necessary to manage a transaction: open the transaction, commit the transaction, and roll back the transaction. The interface only defines the specification, and what we really do depends on its implementation class, so let's take a look at its inheritance relationship.

Implementation class of PlatformTransactionManager

PlatformTransactionManager

A basic class of transaction management provided by AbstractPlatformTransactionManager,Spring provides a template for transaction management and implements a standard process of Spring transaction management.

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Determine whether a transaction already exists

Apply appropriate transaction propagation behavior

Suspend / resume transactions if necessary

Check whether the transaction is marked as rollback-only when committing

Make appropriate changes during rollback (whether to perform a real rollback / or mark the transaction as rollback-only)

Synchronous callback that triggers registration

Four common subclasses are provided in AbstractPlatformTransactionManager, which are described as follows

A detailed code analysis of the transaction manager will be included in the next article, and this article will have a general understanding of it.

Synchronization Mechanism of transactions in Spring

Transaction-related synchronization mechanisms in Spring can be divided into two categories.

Synchronization of resources

Synchronization of behavior

What is resource synchronization? In a transaction, we often execute multiple SQL at one time (if it is a single SQL, it is not necessary to open the transaction). In order to ensure that all SQL of the transaction can use a database connection, we need to synchronize the database connection with the transaction. At this time, the database connection is a resource synchronized with the transaction.

So what is the synchronization of behavior? Or take the database connection as an example. We need to obtain a database connection before the transaction starts. Similarly, when the transaction commits, we need to close the connection (not necessarily really close. If the connection pool is just returned to the connection pool), the behavior of closing the connection also needs to be synchronized with the transaction.

So how does Spring manage synchronization? Similarly, Spring provides a synchronization manager TransactionSynchronizationManager, which is an abstract class where all methods are static, and all methods surround the static constant fields it declares, as follows:

/ / this is the synchronized resource, and Spring uses this to complete the synchronization of the connection private static final ThreadLocal resources = new NamedThreadLocal ("Transactional resources"); / / TransactionSynchronization completes the synchronization of the behavior / / the TransactionSynchronization is analyzed later private static final ThreadLocal synchronizations = new NamedThreadLocal ("Transaction synchronizations"); / / the name of the transaction private static final ThreadLocal currentTransactionName = new NamedThreadLocal ("Current transaction name") / / whether the transaction is marked as read-only private static final ThreadLocal currentTransactionReadOnly = new NamedThreadLocal ("Current transaction read-only status"); / / the isolation level of the thing private static final ThreadLocal currentTransactionIsolationLevel = new NamedThreadLocal ("Current transaction isolation level"); / / whether the transaction private static final ThreadLocal actualTransactionActive = new NamedThreadLocal ("Actual transaction active") is actually opened

You can see that all synchronization is achieved through ThreadLocal. There is no detailed analysis of ThreadLocal in this article, and it doesn't matter if you don't know anything about ThreadLocal. For this article, you only need to know that ThreadLocal can bind resources to the current thread. For example, the attribute ThreadLocal resources represents the binding of a map to the current thread, which provides set and get methods. Used to bind properties to a thread and to get properties bound on a thread, respectively.

All of the above variables except synchronizations should be easy to understand. Synchronizations binds a collection of TransactionSynchronization, so what's the use of this TransactionSynchronization? Let's take a look at its interface definition.

Public interface TransactionSynchronization extends Flushable {/ / transaction completion status / / 0 commit / / 1 rollback / / 2 abnormal status, for example, if an exception occurs during transaction execution and then rollback, the exception / / will be marked as status 2 int STATUS_COMMITTED = 0; int STATUS_ROLLED_BACK = 1; int STATUS_UNKNOWN = 2 / / the TransactionSynchronization we bind needs to be synchronized with the transaction / / 1. If the transaction is suspended, we need to suspend it / / 2. If the transaction is restored, we need to restore it to default void suspend () {} default void resume () {} @ Override default void flush () {} / / some callback methods provided during transaction execution, default void beforeCommit (boolean readOnly) {} default void beforeCompletion () {} default void afterCommit () {} default void afterCompletion (int status) {}}

You can see that this interface defines some methods that can be executed after the transaction reaches different stages, which can be considered to define some callback behavior of the transaction execution process, which is the synchronization of the behavior I mentioned earlier.

Implementation of simulating Spring transaction

In the last part of this article, we want you to simulate the implementation of Spring transactions, and we use the existing AOP to implement transaction management. We use jdbc directly to access the database. Let's make two points clear before simulating.

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

How should the tangent point be defined?

What function does the notification want to achieve?

Let's start with the first question, because we simulate it ourselves, so we set the definition of pointcut as simple as possible, and we might as well specify all the classes under a package directly. For the second problem, we don't get too complicated. We open the transaction before the method executes, commit the transaction and close the connection after the method executes, so we need to define a surround notification. At the same time, we also need to synchronize the connection with the transaction, ensuring that all SQL in the transaction share a transaction is a necessary condition for transaction management. Based on this, we began to write code

We only need to introduce Spring-related dependencies and JDBC-related dependencies. This project is only a Java project in Spring environment, without Web dependency or SpringBoot project. The project structure is as follows:

POM file:

4.0.0 com.dmz.framework mybatis 1.0-SNAPSHOT mysql mysql-connector-java 8.0.15 org.springframework spring-context 5.2.6.RELEASE Org.springframework spring-aop 5.2.6.RELEASE org.aspectj aspectjweaver 1.9.5

Configuration class:

/ / enable AOP and scan components to @ EnableAspectJAutoProxy @ ComponentScan ("com.dmz.mybatis.tx_demo") public class Config {}

Core classes for completing transaction management:

Public class TransactionUtil {public static final ThreadLocal synchronousConnection = new ThreadLocal (); private TransactionUtil () {} public static Connection startTransaction () {Connection connection = synchronousConnection.get () If (connection = = null) {try {/ / replace here with your own connection address connection = DriverManager .getConnection ("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8", "root", "123"); synchronousConnection.set (connection) Connection.setAutoCommit (false);} catch (SQLException e) {e.printStackTrace ();}} return connection;} public static int execute (String sql, Object...) Args) {Connection connection = startTransaction (); try (PreparedStatement preparedStatement = connection.prepareStatement (sql)) {if (args! = null) {for (int I = 1; I < args.length + 1; iTunes +) {preparedStatement.setObject (I, args [I-1]) }} return preparedStatement.executeUpdate ();} catch (SQLException e) {e.printStackTrace ();} return 0;} public static void commit () {try (Connection connection = synchronousConnection.get ()) {connection.commit (); synchronousConnection.remove () } catch (SQLException e) {e.printStackTrace ();}} public static void rollback () {try (Connection connection = synchronousConnection.get ()) {connection.rollback (); synchronousConnection.remove ();} catch (SQLException e) {e.printStackTrace ();}

Classes that actually require transaction management

@ Component public class UserService {public void saveUser () {TransactionUtil.execute ("INSERT INTO `test`.`user` (`id`, `name`) VALUES", 100, "dmz"); / / Test rollback / / throw new RuntimeException ();}}

Section:

@ Component @ Aspect public class TxAspect {@ Pointcut ("execution (public * com.dmz.mybatis.tx_demo.service..*.* (..)") Private void pointcut () {} @ Around ("pointcut ()") public Object around (JoinPoint joinPoint) throws Throwable {/ / Open transaction TransactionUtil.startTransaction () before method execution; / / execute business logic Object proceed = null; try {ProceedingJoinPoint method = (ProceedingJoinPoint) joinPoint; proceed = method.proceed () } catch (Throwable throwable) {/ / rollback TransactionUtil.rollback () when an exception occurs; return proceed;} / / commit transaction TransactionUtil.commit () after method execution is completed; return proceed;}}

The main function used for testing:

Public class Main {public static void main (String [] args) {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext (Config.class); UserService userService = ac.getBean (UserService.class); userService.saveUser ();}}

I will not put down the specific test process and test results. Just copy the code and test it yourself.

This is the end of the content of "summarizing the use of transactions in Spring, abstracting mechanisms and simulating Spring transaction implementation". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

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

Database

Wechat

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

12
Report