[giant's shoulder] 8 scenarios of Spring transaction failure

Use Spring's @ Transactional annotation to control the scenarios in which transactions do not take effect

I don't know if my friends have such an experience. When they are happy to write business code, suddenly the transaction in a method seems to fail. Then, when debug ging tracks the code, it is found that after the execution of the first step of the insert or update statement, there is no changed or saved new data in the database immediately. So I once suspected that the spring transaction failed. So this article will summarize several common scenarios that give you the illusion of "spring transaction failure", and then apply the remedy to the case

The problems encountered in my experience can be divided into the following scenarios:

  1. Whether the database engine supports transactions (the MyIsam engine of Mysql does not support transactions)
  2. Whether the class where the annotation is located is loaded as a Bean (whether it is managed by spring)
  3. Whether the method in which the annotation is located is public modified
  4. Is there a problem with self calling
  5. Is the transaction manager loaded on the data source used
  6. @Is the extension configuration of Transactional correct
The database engine does not support transactions

Take MySQL as an example. Its MyISAM engine does not support transaction operations. InnoDB is the engine that supports transactions. In general, InnoDB is used to support transactions

According to the official MySQL documentation

https://dev.mysql.com/doc/refman/5.5/en/storage-engine-setting.html

Starting from MySQL 5.5.5, the default storage engine is InnoDB, and the previous default is MyISAM, so it should be noted that the underlying engine does not support transactions. No matter what happens, it is useless

Not managed by Spring
// @Service
public class OrderServiceImpl implements OrderService {
 
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }

}

If the @ Service annotation is commented out at this time, this class will not be loaded into a Bean, and this class will not be managed by Spring, and the transaction will naturally become invalid

Method is not public

The following is from the official Spring documentation

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

The general meaning is that @ Transactional can only be used on public methods, otherwise the transaction will not fail. If it is used on non-public methods, you can turn on AspectJ proxy mode

Self call problem
//Example 1
@Service
public class OrderServiceImpl implements OrderService {
 
    public void update(Order order) {
        updateOrder(order);
    }
 
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
 
}

//Example 2
@Service
public class OrderServiceImpl implements OrderService {
 
    @Transactional
    public void update(Order order) {
        updateOrder(order);
    }
 
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateOrder(Order order) {
        // update order
    }
 
}

In example 1, there is no @ Transactional annotation on the update method. Call the updateOrder method with @ Transactional annotation. Does the transaction on the updateOrder method work?
In example 2, the @ Transactional annotation is added to the update method, and the updateOrder method with @ Transactional annotation is called. Is the transaction on the updateOrder method effective?

The answer to these two examples is: neither works!

Because they call themselves, they call their own methods of the class without passing through the Spring proxy class. By default, transactions will take effect only when they are called externally. This is also an old classic problem
One of the solutions to this problem is to inject yourself into your class and call another method with the injected object. This is not very elegant

The data source does not have a transaction manager configured

As shown in the following code, it is useless if the current data source is not configured with a transaction manager

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}
@Transactional's extended configuration does not support transactions

Propagation.NOT_SUPPORTED: indicates that it does not run as a transaction. If there is a transaction, it will be suspended. This means that running in transaction mode is not supported, so even if the transaction takes effect, it is useless!

@Service
public class OrderServiceImpl implements OrderService {
 
    @Transactional
    public void update(Order order) {
        updateOrder(order);
    }
 
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void updateOrder(Order order) {
        // update order
    }
 
}
Abnormal was eaten

This is also a more frequent scenario: eat exceptions and then don't throw them out, and the transaction won't roll back!

@Service
public class OrderServiceImpl implements OrderService {
 
    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
 
        }
    }
 
}
Exception type error

Follow the example above and throw another exception

@Service
public class OrderServiceImpl implements OrderService {
 
    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
            throw new Exception("Update error");
        }
    }
 
}

In this way, the transaction will not take effect, because the default rollback is RuntimeException. If you want to trigger the rollback of other exceptions, you need to configure it on the annotation, such as:

@Transactional(rollbackFor = Exception.class)

This configuration is limited to the Throwable exception class and its subclasses

Copyright notice: This is the original article of CSDN blogger "Yang_yangyang", which follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint.
Original link: https://blog.csdn.net/Yang_yangyang/article/details/114359881

Keywords: Java MySQL Spring

Added by gizzmo on Sun, 23 Jan 2022 22:30:02 +0200