SoFunction
Updated on 2025-03-04

In the same class of Java, when a transaction has no transaction method calls a transaction method, the transaction failure problem

Transaction usage

In the development of spring project, transaction management is realized by adding Transactional annotations to the method, transactions are started when the method starts, transactions are rolled back when an exception occurs, and transactions are submitted before the method ends.

The principle of transaction implementation

TransactionalAnnotations are an important tool used by the Spring framework to implement declarative transaction management. Its principle is mainly based on AOP (sectional programming), and uses dynamic proxy to process transactions before, after, and under exceptional conditions.

When you use it on a method@TransactionalWhen annotated, Spring generates a proxy object at runtime that intercepts calls to the method.

Before the call, the proxy starts a new transaction; after the method execution is completed, the proxy commits or rolls back the transaction, depending on whether the method throws an unhandled exception.

The specific process is as follows:

  • Spring uses jdk dynamic proxy technology or cglib proxy to create proxy objects of target class
  • When the method is called, it is actually called the enhanced method in the proxy class, rather than directly calling the method in the target class
  • Before calling a method, the proxy will be from the annotated attributes (propagation behavior and isolation level) based on the annotation's properties (propagation behavior and isolation level)PlatformTransactionManagerGet a transaction in it and start the transaction before calling the target method
  • Execute target method
  • The target method is executed successfully, the transaction is submitted, and the execution fails, the transaction is rolled back.

Two ways of proxying:

  • JDK Dynamic Proxy: When the target class implements at least one interface, Spring will use JDK dynamic proxy. This proxy is only suitable for interface-based proxy. The principle is to generate a proxy class that implements the target class interface at runtime through the Java reflection mechanism.
  • CGLIB Agent: If the target class does not implement any interface, or you forcefully configure to use CGLIB, Spring uses the CGLIB library to generate a subclass of the target class as a proxy. The principle is to use the CGLIB library to implement the proxy by inheriting the target class and overriding its methods.

reason

In the same class, when a transactionless method directly calls a transactional method, it is called through this. method name.

This way: It directly accesses the implementation of the current object. If the current class is proxyed by Spring AOP, then usethisThe called method will not trigger the AOP facet. That is to say, the sections (such as transaction management, logging, etc.) will not take effect because you directly call the method of the target object, not the method of the proxy object.

How to call beans injected through Autowired annotation:It is called through a proxy object managed by spring container. In this case, the AOP feature can work normally, such as transactions, logs, etc. will take effect.

Generation conditions and timing of proxy objects managed by Spring container

In Spring, the generation of proxy objects is usually related to AOP (sectional-oriented programming) when used on the class@Component@Service@Repositoryor@ControllerWhen using AOP-related annotations on the method, Spring creates proxy objects to be able to execute facet logic when these methods are called.

Common AOP annotations include:

  • @Transactional
  • @Cacheable
  • @Async
  • @Scheduled

Transaction invalidation code

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;

    @Autowired
    private SysConfigMapper configMapper;


    @Override
    public int insertDept(SysDept dept) {
        ("123123");
        (dept);
        insertConfig();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = )
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            ("Configuration" + i);
            (config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

Solution

① Autowire yourself (you can also put the method into another service and inject the service to call it)

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private SysConfigMapper configMapper;
    @Autowired
    private TestTransactionalService testTransactionalService;


    @Override
    public int insertDept(SysDept dept) {
        ("123123");
        (dept);
        ();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = )
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            ("Configuration" + i);
            (config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

② Get the current proxy class through the spring context

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private SysConfigMapper configMapper;
    @Autowired
    private TestTransactionalService testTransactionalService;
    @Autowired
    private ApplicationContext applicationContext;


    @Override
    public int insertDept(SysDept dept) {
        ("123123");
        (dept);
        TestTransactionalService service = ();
        ();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = )
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            ("Configuration" + i);
            (config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

③ Use AopContext to get the current proxy class. You need to add EnableAspectJAutoProxy(exposeProxy = true) to the startup class. exposeProxy = true is used to control the AOP framework to expose the proxy. Only after it is exposed can the current proxy class be obtained through AopContext.

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private SysConfigMapper configMapper;


    @Override
    public int insertDept(SysDept dept) {
        ("123123");
        (dept);
        TestTransactionalService service = (()) ? (TestTransactionalService)() : this;
        ();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = )
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            ("Configuration" + i);
            (config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

Summarize

Use AOP annotations

  • ifTestTransactionalServiceAOP-related annotations are used on the class (such as@Transactional, @Aspect, @Aroundwait)
  • pass()The retrieved object will be a proxy object

The creation of proxy objects is to add additional behavior before and after method calls, such as transaction management, logging, etc.

AOP notes not used

  • If the class does not have any AOP-related annotations
  • The object obtained is an ordinary bean
  • No AOP enhancement

The above is personal experience. I hope you can give you a reference and I hope you can support me more.