Record a small problem you encountered last week.

Transactional backend development involves transactions. One of the most common methods is declarative transaction management using the Spring framework. This is simply adding @Transactional annotations to classes or methods that require Transactional management. Then add on the configuration class @ EnableTransactionManagement annotations (XML configuration USES JavaConfig way here, if it is, is to add in the XML file < tx: annotation – driven / >). The Spring framework then leverages AOP for transaction management before and after related method invocations.

Nothing went wrong until…

Last week I wrote a piece of code about the length of the following sample paper.

public class GiraffeServiceImpl implements GiraffeService {



public void A(List<Giraffe> giraffes) {



for (Giraffe giraffe : giraffes) {

B(giraffe);

}

}



@Transactional("transactionManager")

public void B(Giraffe giraffe) {

// Step 1: update something

// Step 2: insert something

// Step 3: update something

}

}

Copy the code

A Service has A method that calls method B internally. Method A has no transaction management. Method B uses declarative transactions to manage transactions by declaring Transactional annotations on the method.

The transaction for method B is not opened, and the transaction for calling method B is normally opened.

public class GiraffeServiceTest{



@Autowired

private GiraffeService giraffeService;



// The transaction is not started

@Test

public void testA() {

giraffeService.A();

}



// Start transaction normally

@Test

public void testB() {

giraffeService.B();

}

}

Copy the code

T^T

After asking Ming Jia and super elder brother, finally a little understand 🤔

Spring creates a proxy class for the @Transactional target Bean when it loads the target BeanThe target class itself is not aware of the presence of the proxy class. Calls to methods of beans injected through the Spring context are not directly calls to methods of the target class.

not

Instead, the proxy class’s methods are called first, followed by the target class’s.

is

For methods annotated with the @Transactional annotation, the method of the proxy class is called through the interceptor firstTransactionInterceptorThe TransactionInterceptor starts a transaction, then calls the target class’s methods. Finally, when the call is complete, the TransactionInterceptor commits or rolls back the transaction.

transaction manager

In the case of the first code, I call method B from method A using A reference to “this”, which calls the method of the target class directly, not the proxy class obtained from the Spring context, so… Transactions will not start dripping.

The solution is simple: get the Spring context by implementing the ApplicationContextAware interface, then get the target class’s proxy class, and call method B from the proxy class object.

public class GiraffeServiceImpl implements GiraffeService,ApplicationContextAware{



@Setter

private ApplicationContext applicationContext;



public void A(List<Giraffe> giraffes) {



GiraffeService service = applicationContext.getBean(GiraffeService.class);

for (Giraffe giraffe : giraffes) {

service.B(giraffe);

}

}



@Transactional("transactionManager")

public void B(Giraffe giraffe) {

// Step 1: update something

// Step 2: insert something

// Step 3: update something

}

}

Copy the code

@transactional – What happens in background? @Transactional Annotation : Self Invocation

Alas, are almost finished, have not yet come to Wuli Yue son