Detailed explanation of failure reasons of nested transactions in Springboot
- 2021-12-12 04:27:53
- OfStack
First, there are two transaction methods, one of which calls the other.
@Transactional(rollbackFor = Exception.class)
public void trance() {
try {
trance1();// Call under 1 Transaction methods.
} catch (Exception e) {
e.printStackTrace();
}
User user = new User();
ShardingIDConfig shardingIDConfig = new ShardingIDConfig();
user.setId(shardingIDConfig.generateKey().longValue());
user.setName("trance");
user.setSex(0);
userMapper.create(user);
}
@Transactional(propagation = Propagation.REQUIRED)
public void trance1() throws Exception{
User user = new User();
ShardingIDConfig shardingIDConfig = new ShardingIDConfig();
user.setId(shardingIDConfig.generateKey().longValue());
user.setName("trance1");
user.setSex(1);
userMapper.create(user);
System.out.println(user.getId());
throw new RuntimeException();
}
Add dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Then write a test class, which is the first time I have used this test
import com.lijia.App;
import com.lijia.service.UserService;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class Test {
@Autowired
private UserService userService;
@org.junit.Test
public void trance(){
userService.trance();
}
}
Execution will find that RuntimeException is reported, but there are two pieces of data in the database, indicating that the transaction is invalid
runtimeException
Both pieces of data in the database have been uploaded, indicating that the transaction is invalid
Why is this happening
spring transactions use dynamic proxies. Or look at it from the dynamic agent.
Give 1 piece of code
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface IHello {
public void test();
public void test1();
}
class Hello implements IHello{
@Override
public void test() {
System.out.println("test");
}
@Override
public void test1() {
System.out.println("test1");
}
}
public class MyInvoke implements InvocationHandler{
public Object target;
public MyInvoke(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().contains("test")){
System.out.println("======== Acting =======");
}
return method.invoke(target,args);
}
public static void main(String[] args) {
MyInvoke myInvoke = new MyInvoke(new Hello());
IHello iHello = (IHello) Proxy.newProxyInstance(MyInvoke.class.getClassLoader(),new Class[]{IHello.class},myInvoke);
iHello.test();
iHello.test1();
}
}
Put the above
Hello类
In
test1方法
Put in
test方法
Medium
public void test() {
test1();
System.out.println("test");
}
Going back to the above question, you will find that
trance1()
There is no proxy, so both operations are inserted into the database.
You need to get the current proxy object and call
trance1()
Pass
AopContext.currentProxy()
Get the current agent
((UserService)AopContext.currentProxy()).trance1();
Change to call like this
trance1()
Run Test, and then there is only one piece of data left in the database, indicating that
trance1()
Method is rolled back.
The above is the analysis of Springboot nested transaction failure reasons detailed explanation of the details, more on Springboot nested transaction failure analysis of information please pay attention to other related articles on this site!