The @Transactional transaction that Spring officially recommends, I still don't recommend you to use it!

Recommended reading:

Transaction management is an indispensable part of system development. Spring provides a good transaction management mechanism, which is mainly divided into two types: programmatic transaction and declarative transaction.

Basic knowledge about transactions, such as what is a transaction, database transaction and Spring transaction ACID, isolation level, propagation mechanism, behavior, etc., will not be introduced in detail in this article. By default, everyone has a certain understanding.

In this article, the author will first briefly introduce what declarative transactions and programmatic transactions are, and then talk about why I do not recommend using declarative transactions.

Programmatic transaction

Based on the underlying APIs, such as PlatformTransactionManager, TransactionDefinition, TransactionTemplate and other core interfaces, developers can perform transaction management programmatically.

Programmatic transaction mode requires developers to manually manage transactions such as opening, committing, and rolling back in the code.

public void test() {

      TransactionDefinition def = new DefaultTransactionDefinition();

      TransactionStatus status = transactionManager.getTransaction(def);



       try {

         // 事务操作

         // 事务提交

         transactionManager.commit(status);

      } catch (DataAccessException e) {

         // 事务提交

         transactionManager.rollback(status);

         throw e;

      }

}

Like the above code, developers can control transactions themselves through API.

Declarative transaction

The declarative transaction management method allows developers to manage transactions with the help of configuration without the need to rely on the underlying API for hard coding. Developers can use only annotations or configuration-based XML to manage transactions.

@Transactional

public void test() {

     // 事务操作  

}

As above, using @Transactional can add transaction control to the test method.

Of course, the above code is only simplified, and you need some configuration content if you want to use transactions. I won't elaborate on it here.

Each of these two types of transactions has its own advantages and disadvantages. So, what are the suitable scenarios for each? Why would anyone refuse to use declarative transactions?

Advantages of declarative transactions

Through the above example, in fact, we can easily see that declarative transactions help us save a lot of code. It will automatically help us open, commit and roll back transactions, freeing programmers from transaction management. .

Declarative transaction management is implemented using AOP, and the essence is to intercept before and after the target method is executed.  Add or create a transaction before the execution of the target method. After the execution of the method, choose to commit or roll back the transaction according to the actual situation.

In this way, it is not intrusive to the code, and only business logic needs to be written in the method.

But are declarative transactions really so good? Not necessarily.

The granularity of declarative transactions

First of all, there is a limitation of declarative transactions, that is, its minimum granularity should be applied to the method.

In other words, if you want to add transactions to a part of the code block, you need to separate this part of the code block as a method.

However, it is precisely because of this granularity that I do not recommend excessive use of declarative transactions.

First of all, because declarative transactions are implemented through annotations, and sometimes they can be implemented through configuration, this can lead to a problem, that is, this transaction may be ignored by developers.

What's the problem with the transaction being ignored?

First of all, if the developer does not notice that a method is nested by transaction, then some operations such as RPC remote call, message sending, cache update, file writing, etc. may be added to the method.

We know that if these operations are wrapped in a transaction, there are two problems:

1. These operations themselves cannot be rolled back, which will cause data inconsistencies. Maybe the RPC call was successful, but the local transaction was rolled back, but the PRC call cannot be rolled back.

2. There are remote calls in the transaction, which will lengthen the entire transaction. So long will cause the database connection of this transaction to be occupied all the time, so if there are too many similar operations, the database connection pool will be exhausted.

Sometimes, even if there is no remote operation in the transaction, some people may still inadvertently perform some memory operations, such as operations. Or if you encounter sub-database and sub-table, you may inadvertently perform cross-database operations.

But if it is a programmatic transaction, the business code will clearly see where to start the transaction, where to commit, and when to roll back. In this way, when someone changes this code, he will be forced to consider whether the code to be added should be in the method transaction.

Some people may say that there are already declarative transactions, but the people who wrote the code didn't pay attention. Who can blame it.

Although this is the case, we still hope that some mechanisms or regulations can be adopted to reduce the probability of these problems.

For example, it is recommended that you use programmatic transactions instead of declarative transactions. Because the author has worked for so many years, there have been more than one failures caused by developers not paying attention to declarative transactions.

Because sometimes, declarative transactions are really not obvious enough.

Wrong use of declarative transactions is easy to fail

In addition to the issue of transaction granularity, there is also a problem that although declarative transactions seem to help us simplify a lot of code, once they are useless, they can easily cause transactions to fail.

The following scenarios may cause declarative transactions to fail:

1. @Transactional is applied to non-public modified methods

2. @Transactional annotation property propagation is set incorrectly

3. @Transactional annotation attribute rollbackFor is set incorrectly

4. Method calls in the same class cause @Transactional to fail

5. Exception caught by catch causes @Transactional to fail

6. The database engine does not support transactions

Many of the above problems can be avoided if programmatic transactions are used.

The problem of using declaring transaction invalidation has happened many times. I don’t know if you have encountered it, I have actually encountered it

Because Spring's transactions are implemented based on AOP, but in the code, sometimes we have many aspects, different aspects may deal with different things, and multiple aspects may affect each other.

In a previous project, I found that all of our Service layer transactions failed. After a SQL execution failed, it was not rolled back. After investigation, I found that it was because a colleague added a new aspect, which was done in this aspect. The unified capture of these exceptions results in no exceptions being caught in the transaction aspect, resulting in the transaction cannot be rolled back.

This kind of problem has happened more than once, and it is not easy to find.

Many people will still say that, in the final analysis, they are still incapable and do not understand the affairs thoroughly. Who can be blamed if they are used incorrectly.

But I still said that, we really cannot guarantee that everyone's abilities are high , and we cannot require all developers to be error-free. What we can do is to try to avoid or reduce the probability of these problems through mechanisms or regulations.

In fact, if you have carefully read the Java Development Manual published by Alibaba, you can actually find that many of the regulations are not completely easy to understand, and some are relatively rigid, but in fact, these specifications are all Summarized by the developers who crawled out of countless pits.

Regarding the usage of @Transactional, it is also mentioned in the statute, but the views in the statute are not as clear as mine:

to sum up

Finally, I believe that many people don't necessarily agree with the views in this article. Many people will say: Spring officials recommend non-intrusive declarative transactions. What qualifications do you have to come out of BB?

To be honest, in the first few years of my work, I was also keen on using declarative transactions, and I found it very clean and "elegant". I feel that the use of program-style affairs is superfluous, and there is no craftsmanship.

But slowly, after several problems occurred online, we found after replaying that many times the code you wrote yourself was very elegant, which was completely fine.

However, the elegance also brings some side effects. The brothers cannot criticize me because my usage is indeed correct...

Therefore, there are some things that I will not know until after the pain.

Of course, this article does not require that you must not use declarative transactions at all. It is just that when you use transactions in the future, you can consider the points mentioned in this article and choose your own.

Guess you like

Origin blog.csdn.net/weixin_45784983/article/details/109306913