Why spring transaction does not take effect, rollback fails, and transaction try catch (transaction fails)

Principles of Spring transactions

The essence of Spring transactions is database InnoDB supports transactions. Without InnoDB, spring cannot provide transaction support. The transaction submission and rollback of the real database layer are realized through binlog or redo log.

For a pure jdbc operation database, if you want to use transactions, you need to follow the following steps:

  1. Get connection connection = drivermanager getConnection(url, username, root);
  2. Open the transaction connection setAutoCommit(true/false);
  3. Execute CRUD
  4. Commit transaction / rollback transaction connection commit() / connection . rollback();
  5. Close the connection close();

The code is as follows

 	try {
            Connection connection = DriverManager.getConnection(url, username, root);
            connection .setAutoCommit(false); //Enable transaction and prohibit automatic submission
            preparedStatement = conn.prepareStatement("update t_category t set t.name='test' where t.id =?");
            preparedStatement.setInt(1, 10);
            preparedStatement.executeUpdate() ;
            connection .commit(); //Commit transaction
            }catch(Exception e ){
				connection .rollback();
			}
copy

After using the transaction management function of Spring, we can no longer write the code of steps 2 and 4, but be completed automatically by Spirng.

Transaction mechanism of Spring

Spring's transaction mechanism provides a PlatformTransactionManager interface. Transactions of different data access technologies are implemented with different interfaces, as shown in the table:

Data access technology

realization

JDBC

DataSourceTransactionManager

JPA

JapTransactionManager

Hibernate

HibernateTransactionManager

JDO

HibernateTransactionManager

Distributed transaction

JtaTransactionManager

Above references: https://my.oschina.net/xiaolyuh/blog/3109049

Take JDBC as an example, you can configure the transaction manager in the code:

 @Bean
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(druidDataSource());
        return transactionManager;
    }
copy

The @ Transactional annotation indicates on the method that the method needs transaction support. This is an implementation operation based on AOP.

AOP agent:

1.JDK dynamic proxy implementation (the principle is to use reflection mechanism)

There are four steps as follows:

  1. Create your own calling processor by implementing the InvocationHandler interface;
  2. Create a dynamic Proxy class by specifying a ClassLoader object and a set of interfaces for the Proxy class; ' The constructor of the dynamic Proxy class is obtained through the reflection mechanism, and its only parameter type is the calling processor interface type; When a Proxy class is created by passing in a constructor as a parameter to the handler class.

2.CGLIB agent

1. If the target object implements the interface, the dynamic agent of JDK will be used to implement AOP by default. 2. If the target object implements the interface, CGLIB can be forced to implement AOP. 3. If the target object does not implement the interface, CGLIB library must be used, and spring will automatically convert between JDK dynamic agent and CGLIB

Database isolation level

Isolation level

Value of isolation level

Problems caused by

Read-Uncommitted

0

Cause dirty reading

Read-Committed

1

Avoid dirty reading and allow non repeatable reading and phantom reading

Repeatable-Read

2

Avoid dirty reading, do not repeat reading, and allow phantom reading

Serializable

3

Serial reading, transactions can only be executed one by one, avoiding dirty reading, non repeatable reading and unreal reading. Slow execution efficiency and careful use

Seven propagation properties of Spring transactions

Constant name

Constant interpretation

PROPAGATION_REQUIRED

The current transaction is supported. If there is no current transaction, a new transaction will be created. This is the most common choice and the default transaction propagation of Spring.

PROPAGATION_REQUIRES_NEW

Create a new transaction. If there is a current transaction, suspend the current transaction. The newly created transaction will have nothing to do with the suspended transaction. It is two independent transactions. After the outer transaction fails to roll back, the execution result of the inner transaction cannot be rolled back. The inner transaction fails to throw an exception, and the outer transaction is caught. The rollback operation can also not be processed

PROPAGATION_SUPPORTS

The current transaction is supported. If there is no current transaction, it will be executed in a non transaction manner.

PROPAGATION_MANDATORY

Supports the current transaction. If there is no current transaction, an exception will be thrown.

PROPAGATION_NOT_SUPPORTED

Perform operations in a non transactional manner. If there is a transaction currently, suspend the current transaction.

PROPAGATION_NEVER

Execute in a non transactional manner. If there is a transaction currently, an exception will be thrown.

PROPAGATION_NESTED

If an active transaction exists, it runs in a nested transaction. If there is no active transaction, it is executed according to the REQUIRED attribute. It uses a single transaction with multiple savepoints that can be rolled back. Rollback of internal transactions will not affect external transactions. It works only on the DataSourceTransactionManager transaction manager.

Note: the default transaction propagation feature of Spring is PROPAGATION_REQUIRED, MySQL The default isolation level is repeatable read

Nested examples of transactions

package com.yudianxx.springBootDemo.transation;

import com.yudianxx.springBootDemo.mapper.image.ImageCategoryMapper;
import com.yudianxx.springBootDemo.model.image.Category;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;


@Service
@Slf4j
public class TransactionTestServiceImpl implements TransactionTestService {


    @Autowired
    TransactionTestServiceImpl transactionTestService;

    @Autowired
    ImageCategoryMapper imageCategoryMapper;

    /**
     * Transaction test
     *
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void testTransactional() {
        Category category = Category.builder().name("Transaction test").build();
/*//       Case 1
        try {
            a(category);  //The internal class calls without AOP, and the transaction does not work. An error is reported when adding a(), and the insertion is still valid, which is equivalent to an ordinary call
            b(category);
        } catch (Exception e) {
            e.printStackTrace();
        }*/

/*
//        Case 2
        transactionTestService.a(category);  //Solve the problem of situation 1
        transactionTestService.b(category);
//        throw new RuntimeException();   //If there is no try catch and the parent and child are in the same transaction, the parent will report an error and roll back all transactions
*/

/*//        Situation 3
        try{
            transactionTestService.a(category);
            transactionTestService.b(category);
            throw new RuntimeException();  //For the same transaction between the parent and the child, the child method does not throw an exception. Although the parent throws an exception but is caught, it is equal to that it has not been thrown, so it will not be rolled back
        }catch (Exception e){

        }*/


/*//          Situation 4
        try {
            transactionTestService.a(category);
            transactionTestService.b(category);
            throw new RuntimeException();
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //Force rollback if the child is requirements_ New, the child is not rolled back
        }*/

/*//          Case 5
        try {
            transactionTestService.a(category);
            transactionTestService.b(category);
            throw new RuntimeException();
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //Force rollback if the child is requirements_ New, the child is not rolled back
        }*/

//          Situation 6
        try {
            transactionTestService.a(category);
            transactionTestService.b(category);
//            transactionTestService.c(category);  // No rollback
//           transactionTestService.d(category); // RollBACK 
//           transactionTestService.e(category); // No rollback e is another transaction
//           transactionTestService.f(category); //a. b no rollback, f rollback
            transactionTestService.g(category); //a. b and g are not rolled back
        } catch (Exception e) {

        }

    }

    //    @Transactional(propagation = Propagation.REQUIRES_NEW)
// A separate transaction will be started. If it succeeds, it will be inserted and will not be affected by other transactions
    @Transactional(propagation = Propagation.REQUIRED)
    public void a(Category category) {
        log.info("get into A method");
        category.setName("Transaction test a");
        imageCategoryMapper.insert(category);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void b(Category category) {
        log.info("get into B method");
        category.setName("Transaction test b");
        imageCategoryMapper.insert(category);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void c(Category category) {
        log.info("get into c method");
        try {
            int j = 1 / 0;
        } catch (Exception e) {
//            throw e;  // If the error is thrown, the upper layer will roll back the transaction if it is caught. If there is no throw, the method will not throw the exception after handling the exception itself, which is equivalent to the normal execution without throwing the exception
            //In short, throw e is equivalent to not adding try catch
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void d(Category category) {
        log.info("get into d method");
        int j = 1 / 0;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void e(Category category) {
        log.info("get into e method");
        int j = 1 / 0;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void f(Category category) {
        log.info("get into f method");
        category.setName("Transaction test f");
        imageCategoryMapper.insert(category);
        int j = 1 / 0;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void g(Category category) {
        log.info("get into g method");
        category.setName("Transaction test g");
        imageCategoryMapper.insert(category);
        try {
            int j = 1 / 0;
        } catch (Exception e) {

        }
    }
}
copy

PROPAGATION_REQUIRED(spring default)

In case 6, transactiontestservice A (category) when spring has started a transaction, call transactiontestservice b(category), transactionTestService.b(category) sees that it is already running in transactiontestservice Within the transaction of a (category), no new transaction will be created and added to the transaction of A.

PROPAGATION_REQUIRES_NEW

In case 6, transactiontestservice E (category) is a new transaction, the transactions of a and b will be suspended, and e will start a new transaction. a. b. e does not roll back, mainly depending on whether an exception is thrown.

When does spring roll back transactions?

By default, the declarative transactions of Spring and EJB will trigger the transaction rollback unchecked exception after throwing the unchecked exception, that is, the runtime exception runntimeException rolls back the transaction;

checked exceptions, i.e. exceptions that can be try{} caught, will not be rolled back Of course, you can also configure spring parameters to roll back The configuration is as follows:

@Transactional( rollbackFor = Exception.class)
copy

The transaction boundary of spring starts before calling the business method. After the business method is executed, commit or rollback is executed (spring default depends on whether runtime exception is thrown) If a runtime exception is thrown and there is no catch in your business method, the transaction will be rolled back. Generally, you don't need to catch exceptions in business methods. If you have to catch, you must throw runtime exception after you finish the work you want to do (such as closing files, etc.), otherwise spring will commit your operation, which will produce dirty data So your catch code is icing on the cake.

Conclusion:

  1. No matter the non RuntimeException error is reported internally or externally, it will not be rolled back.
  2. If you add rollbackfor = exception Class, no matter how internal and external errors are reported, they will be rolled back.

reference resources: https://my.oschina.net/xiaolyuh/blog/3109049 https://www.cnblogs.com/pjjlt/p/10926398.html

Article participation Tencent cloud we media sharing plan , you who love writing are welcome to join us!
This article is shared from the author's personal site / blog: https://blog.csdn.net/yudianxiaoxiao
In case of infringement, please contact yunjia_community@tencent.com Delete.

Posted by trev on Fri, 06 May 2022 12:13:49 +0300