preface

Spring transaction management is almost universally used in projects, but are we using it correctly? Is the principle really known? This article will quickly explain how Spring transaction failures work in combination with business scenarios

1 Service Scenario

If there is such A business, the save method of class A needs to call the save2 method of the same class. The success or failure of the save2 method does not affect the execution of the save method. Therefore, we would like to set the transaction propagation behavior of save2 to REQUIRES_NEW, as follows:

@Service @Slf4j public class UserService { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private UserService2 userService2; @Transactional public void save() { jdbcTemplate.execute("INSERT INTO user (id, name) VALUES\n" + "(5, 'Jack5')"); try { save2(); } catch (Exception e) {system.err.println (" error "); } } @Transactional(propagation = Propagation.REQUIRES_NEW) public void save2() { jdbcTemplate.execute("INSERT INTO user (id, name) VALUES\n" + "(6, 'Jack6')"); int i = 1 / 0; }}Copy the code

Since save2 method cannot affect the execution of save method, it is necessary to supplement save2 method. The expected result is that the save method inserts data normally and the SAVe2 method fails

Execution Result:

2 inquiry

2.1 Spring propagation Behavior

Public enum Propagation {REQUIRED(0), SUPPORTS(1), MANDATORY(2), REQUIRES_NEW(3), NOT_SUPPORTED(4), NEVER(5), NESTED(6); } REQUIRED: Join a transaction if it currently exists; If there is no transaction currently, a new transaction is created. SUPPORTS: Joins a transaction if it currently exists. If there is no transaction currently, it continues in a non-transactional manner. MANDATORY: Joins a transaction if it currently exists. If there is no transaction currently, an exception is thrown. REQUIRES_NEW: Creates a new transaction and suspends the current one if one exists. NOT_SUPPORTED: Run non-transactionally, suspending the current transaction if one exists. NEVER: Runs non-transactionally and throws an exception if a transaction currently exists. NESTED: Creates a transaction to run as a NESTED transaction of the current transaction if a transaction exists. If there are no transactions, this value is equivalent to REQUIRED.

To be sure, the business does use REQUIRES_NEW. But why does it fail?

2.2 Dynamic Proxy

Before continuing, let’s briefly cover dynamic proxies. The proxy pattern is a design pattern that enhances the creation of methods in a class. The proxy mode is divided into dynamic proxies, whose proxy classes are generated at run time, and static proxies, which are generated at compile time. Dynamic proxies can be divided into JDK dynamic proxies based on interfaces and Cglib dynamic proxies based on classes.

The java.lang.Reflect package provides a Proxy class and an InvocationHandler interface to generate JDK dynamic Proxy classes and dynamic Proxy objects.

public interface Person { void work(); } public class Student implements Person {@override public void work() {system.out.println (" 教 学 "); }} Public class implements InvocationHandler {private Person Person; public MyInvocationHandler(Person person) { this.person = person; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {system.out.println (" eat ----- before reading "); method.invoke(person, args); return null; } } public class Main { public static void main(String[] args) { Person person = new Student(); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(person); System.out.println(Arrays.toString(Student.class.getInterfaces())); Person proPerson = (Person) Proxy.newProxyInstance(Student.class.getClassLoader(), Student.class.getInterfaces(), myInvocationHandler); proPerson.work(); }}Copy the code

The result is: have a meal —– read a book again

The detailed code can be downloaded at github, github.com/229319258/s…

2.3 Dynamic proxy pit

At this point, we know that Spring transactions are implemented based on dynamic proxies. So what does the real reason for Spring transaction failure have to do with dynamic proxies?

To simulate the problem of Spring transaction failure, modify the above code slightly,

Public class Student implements Person {@override public void work() {system.out.println (" 教 学 "); try { this.work2(); } catch (Exception e) {}} public void work2() {system.out.println (); int i = 1 / 0; }}Copy the code

If we focus on the try block, we can see that the Student instance is actually calling work2, not the so-called enhanced class of Work2. Similarly, the save2 method of Spring transaction failure above is invoked not by a proxy class, but by an unenhanced ordinary object, UserService.

Therefore, without proxy-generated methods, Spring transactions will of course fail

So, here comes the question. If I do want the save2 transaction to work, what should I do? There are two ways to do this

  • Put save2 back on another class
  • Get the currentProxy object using the method aopcontext.currentproxy ()
@Transactional public void save() { jdbcTemplate.execute("INSERT INTO user (id, name) VALUES\n" + "(5, 'Jack5')"); try { UserService proxy = (UserService) AopContext.currentProxy(); proxy.save2(); } catch (Exception e) {system.err.println (" error "); }}Copy the code

3 conclusion

The @Transactional(Propagation = Propagation.REQUIRES_NEW) class cannot be called Transactional(Propagation = Propagation.

In addition to this failure, you cannot call a @Transactional method in the same class as a proxy method that has not yet been set up for Transactional functions. Such as:


    //    @Transactional
    public void save() {
        save2();
    }

    @Transactional()
    public void save2() {
        jdbcTemplate.execute("INSERT INTO user (id, name) VALUES\n" +
                "(7, 'Jack7')");
        int i = 1 / 0;
    }
Copy the code

Looking at the database data, again the data of save2 is not rolled back because instead of calling the proxy class, the normal this(UserService) method is called. Therefore, the transaction is also invalidated.

Is there a feeling that you want to take a look at your code right away? -. –

The code address

The code address

reference

Java dynamic proxy implementation and principle detailed analysis of Spring transaction failure that point

Room cleaning

Guangzhou Reed Technology Java development team

Reed Technology – Guangzhou professional Internet software service company

Seize every detail, create every beauty

Follow our official account to learn more

Want to fight with us? Lagou search for “Reed Technology” or join us by sending your resume to [email protected]

Follow us, your comments and likes support us the most