Welcome to the public number [sharedCode] committed to mainstream middleware source code analysis, personal website: www.shared-code.com/

1. Implementation method and principle of Spring transaction

The essence of Spring transactions is the transaction support of the database. Without the transaction support of the database, Spring cannot provide transaction functionality. In the real database layer, transaction commits and rollbacks are committed after binlog commits: redo log, undo log rollback.

The Transactional annotation that we use in our programs is @Transactional annotations on methods, which are declarative transactions.

The essence of declarative transaction is to intercept the method before and after using AOP functions, weaving the function of transaction processing into the interception method, that is, adding a transaction before the target method starts, and submitting or rolling back the transaction according to the execution of the target method after it is completed.

2. The database itself does not support transactions

Here take MySQL as an example, its MyISAM engine does not support transaction operations, InnoDB is the engine that supports transactions, generally to support transactions will use InnoDB

3. Invocation of the current class

@Service
public class UserServiceImpl implements UserService {

    public void update(User user) {
        updateUser(user);
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(User user) {
        // update user}}Copy the code

There will be no transaction management operations in this case.

Spring uses an AOP Transactional approach that essentially uses dynamic proxies to manage transactions. The @Transactional method ona class called Transactional has no effect because it calls this method.

OK, we’re looking at an example below.

@Service
public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)
    public void update(User user) {
        updateUser(user);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUser(User user) {
        // update user}}Copy the code

“Transactional” (@transactional); “REQUIRES_NEW” (updateUser);

The answer is: it doesn’t work!

Calling the class’s own methods because they are called by themselves, while not going through Spring’s proxy classes, by default, only invoke transactions from outside the class, is a classic cliche.

4. Methods are not public

@Service
public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)
    private void updateUser(User user) {
        // update user}}Copy the code

Private methods are not propped up by Spring, so no transactions occur, which is ineffective.

5. Not managed by Spring

//@Service
public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)
    public void updateUser(User user) {
        // update user}}Copy the code

Spring can’t even generate proxy objects without beans managed by Spring.

6. The configured transaction propagation is faulty

@Service
public class UserServiceImpl implements UserService {

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void update(User user) {
        // update user}}Copy the code

Review Spring’s transaction propagation behavior

The propagation behavior of Spring transactions describes how Spring handles the behavior of multiple transactions when they exist at the same time.

  1. PROPAGATION_REQUIRED: Create a new transaction if there is no transaction currently, and join the transaction if it currently exists. This setting is the most commonly used.
  2. PROPAGATION_SUPPORTS: Supports the current transaction. If a transaction currently exists, join it, or execute it as a non-transaction if no transaction currently exists
  3. PROPAGATION_MANDATORY: The current transaction is supported. If a transaction exists, it is added, and if no transaction exists, an exception is thrown.
  4. PROPAGATION_REQUIRES_NEW: Creates a new transaction, regardless of whether one currently exists.
  5. PROPAGATION_NOT_SUPPORTED: Executes an operation in a non-transactional manner, and suspends the current transaction, if one exists.
  6. PROPAGATION_NEVER: Executes non-transactionally, throws an exception if a transaction currently exists.
  7. PROPAGATION_NESTED: Executes within a nested transaction if a transaction currently exists. If there are no transactions currently, the REQUIRED attribute is executed

A transaction may fail when propagation actions are set to PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER, and PROPAGATION_SUPPORTS

7. You caught the exception

@Service
public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)
    public void update(User user) {
        
      try{
        // update user
      }catch(Execption e){
         log.error("Abnormal",e)
      }
    }    
}
Copy the code

If the exception is caught, there is no way for the proxy class to know whether you have an error or need to roll back, so there is no way to roll back this case.

8. Declarative transactions at the interface layer use cglib proxies

public interface UserService   {

    @Transactional(rollbackFor = Exception.class)
    public void update(User user)  
}
Copy the code
@Service
public class UserServiceImpl implements UserService {

    
    public void update(User user) {
        // update user}}Copy the code

The element’s “proxy-target-class” attribute values control whether interface-based or class-based proxies are created. If the “proxy-target-class” genus value is set to “true”, class-based proxies will come into play (this requires the CGLIB library cglib.jar in the CLASSPATH). If the “proxy-target-class” genus value is set to “false” or this property is omitted, the standard JDK interface-based proxy takes effect

The @Transactional Cglib annotation differs from the Java dynamic proxy in that the proxy target object does not implement the interface. Therefore, annotations written to interface methods are disabled when using the Cglib proxy. For compatibility, annotations should always be written to implementation class methods.

9. The rollbackFor exception is incorrectly specified

@Service
public class UserServiceImpl implements UserService {

    @Transactional
    public void update(User user) {
        // update user}}Copy the code

The default rollback exception is RuntimeException, and the transaction will not be rolled back if any other exception occurs