Transactional @Transactional is an annotation that we almost never escape when we use Spring to declare transactions. Its implementation principle is through Spring AOP before and after annotation modification method woven into the transaction management implementation statement, so developers only need to pass a single annotation can replace a series of tedious transaction start, transaction close and other repetitive coding tasks.

The coding is simple, but because it hides the intuitive implementation logic, some of the wrong coding methods can invalidate the @Transactional annotation and make it less Transactional. The most direct manifestation is that an exception is thrown during method execution, but the transaction is not rolled back, resulting in dirty data.

I wrote an interesting discussion on my blog earlier and I’m going to ask: Is this transaction going to roll back? , at that time a lot of people gave the standard wrong answer, if you haven’t seen the little partner might as well go in to challenge?

Although we discussed some special cases before, some friends still ask questions about transaction failure in emails and wechat groups all the time. Transactional Transactional states that transactions fail in all sorts of ways! So, write a summary today, and if you come across it again next time, open this article and go down one by one to see if there is something wrong. Of course, there may be omissions, so if you have any other mistakes, please let me know, and I will continue to cover them in this article.

1. Call in the same class

Wrong examples:

public class A {
    
    public void methodA(a) {
        methodB();
        
        // Other operations
    }

    @Transactional
    public void methodB(a) {
        // Write database operations}}Copy the code

This error applies to all annotations based on Spring AOP implementations, such as: The @Async annotation mentioned in implementing Asynchronous calls with @Async, the @Scheduled annotation mentioned in Implementing Scheduled Tasks with @Scheduled, and the @Cacheable annotation mentioned in Spring caching annotations.

The solution to this problem is relatively simple, or a reasonable planning of hierarchical relations, such as:

@Service
@AllArgsConstructor
public class A {
    
    private B b;
    
    public void methodA(a) {
        b.methodB();
        // Other operations}}@Service
public class B {

    @Transactional
    public void methodB(a) {
        // Write database operations}}Copy the code

Note: Here class A uses an implementation of constructor injection B (why not use @Autowrire, check out this post A few days ago when not to use @Autowired injection), The constructors are generated using Lombok’s @allargsconstructor (see the previous article Lombok: Making JAVA Code More elegant if you’re not familiar with it).

2. The @transactional modifier is not public

Wrong examples:

public class TransactionalMistake {
    
    @Transactional
    private void method(a) {
        // Write database operations}}Copy the code

This is what Spring AOP based annotations are supposed to do. This is the simplest one, and it’s easy to understand, and it’s intuitive, so I won’t go into all the details. Simply change the method access type to public.

3. Different data sources

Wrong examples:

public class TransactionalMistake {

    @Transactional
    public void createOrder(Order order) { orderRepo1.save(order); orderRepo2.save(order); }}Copy the code

Sometimes, we may write to multiple data sources at the same time, such as orderRepo1 and orderRepo2 in the above example, which are connected to two different data sources. By default, such transactions across data sources will not succeed.

If you want to implement transactions between multiple data sources, you can introduce JTA. For details on how to do this, check out the previous post on Transaction Management for Multiple Data Sources using JTA.

4. The rollback configuration is incorrect

By default, only RuntimeException and Error are rolled back. If they and their descendants are not abnormal, they will not roll back.

Therefore, when customizing exceptions, you should plan appropriately. If you want to affect transaction rollback, you can define them as RuntimeException subclasses. If it is not a RuntimeException, but you also want to trigger a rollback, you can use the rollbackFor attribute to specify the exception to rollback.

public class TransactionalMistake {

    @Transactional(rollbackFor = XXXException.class)
    public void method(a) throws XXXException {}}Copy the code

5. Database engine does not support transactions

This comes from a reader feedback example, the code is exactly the same as my case, my side is good, but he just won’t roll back.

It was later detected because a key attribute was missing:

spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
Copy the code

The spring.jpa.database-platform configuration here is mainly used to set the dialect used by Hibernate. Mysql > MySQL5InnoDBDialect mysql > MySQL5InnoDBDialect mysql > MySQL5InnoDBDialect mysql > MySQL5InnoDBDialect mysql > MySQL5InnoDBDialect mysql > MySQL5InnoDBDialect mysql > MySQL5InnoDBDialect The MyISAM storage engine has no transactions.

If your transaction doesn’t work, you can check to see if the created table uses the MyISAM storage engine. If so, that’s the reason!

summary

If you get to the end and see anything else that hasn’t been included, feel free to let us know and we’ll keep this post updated! To help readers with such problems.

Well, that’s all for today’s learning! If you have any difficulty in learning? You can join our super high quality Spring technology exchange group, participate in the exchange and discussion, better learning and progress!

Welcome to pay attention to my public account: program ape DD, share the outside can not see the dry goods and thinking!