事务传播特性汇总:
required:使用当前的事务,如果当前没有事务,则创建一个事务,子方法是必须运行在一个事务中的,如果当前存在事务,则加入这个事务,成为一个整体。
supports:如果当前有事务,则使用事务,如果当前没有事务,则不使用事务
mandatory:该传播属性强制必须存在一个事务,如果不存在,则抛出异常
required_new:如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;如果当前没有事务,则同required
not_support:如果当前有事务,则把事务挂起,自己不使用事务去进行数据库操作
never:如果当前事务存在,则抛出异常
nested: 如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同required
但是如果父事务提交,则会携带子事务一起提交。如果父事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以不会滚(捕获子事务的异常),也可以回滚。
有异常才会回滚事务,如果异常被捕获,也就是try,catch,就不会回滚事务了,
发生异常,或者抛出异常都会回滚事务
准备工作
演示required
每次测试之前都会清空数据库
导入test依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
编写test类
import com.imooc.Application;
import com.imooc.service.impl.TestTransService;
import org.junit.Test;
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 = Application.class)
public class TransTest {
@Autowired
private TestTransService testTransService;
/*
* 事务传播特性
* required:
* supports:
* mandatory:
* required_new:
* not_supported:
* never:
* nested:
* */
@Test
public void demo(){
testTransService.saveParent();
testTransService.saveChildren();
}
}
编写需要的测试方法
package com.imooc.service.impl;
import com.imooc.mapper.StuMapper;
import com.imooc.pojo.Stu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TestTransService {
@Autowired
private StuMapper stuMapper;
public void saveParent(){
Stu stu=new Stu();
stu.setName("parent");
stu.setAge(55);
stuMapper.insert(stu);
}
public void saveChildren(){
saveChild1();
int a=1/0;
saveChild2();
}
public void saveChild1(){
Stu stu1=new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2(){
Stu stu2=new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
}
没有开启事务,直接测试
报错:java.lang.ArithmeticException: / by zero
结论:在报错之前,已经执行的代码会进行数据库操作,之后代码没有执行,也就没有操作数据库
在demo()方法上开启事务,进行测试
import com.imooc.Application;
import com.imooc.service.impl.TestTransService;
import org.junit.Test;
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;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TransTest {
@Autowired
private TestTransService testTransService;
/*
* 事务传播特性
* required:
* supports:
* mandatory:
* required_new:
* not_supported:
* never:
* nested:
* */
@Transactional(propagation = Propagation.REQUIRED)
@Test
public void demo(){
testTransService.saveParent();
testTransService.saveChildren();
}
}
报错:java.lang.ArithmeticException: / by zero
结论:事务具有传播特性,demo()方法开启事务,方体体里面的方法也具有事务,所以报错后数据库进行了回滚操作
如果demo方法没有开启事务,在saveChildren()上开启了事务( @Transactional(propagation = Propagation.REQUIRED))
,则会保存saveParent()方法插入的数据。
required:使用当前的事务,如果当前没有事务,则创建一个事务,子方法是必须运行在一个事务中的,如果当前存在事务,则加入这个事务,成为一个整体。
================================================================================================
演示required_new
spring boot测试环境会自动回滚,不适合测试事务,直接使用开发环境,然后用postman调用
controller
package com.imooc.controller;
import com.imooc.service.impl.TestTransService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestTransController {
@Autowired
private TestTransService testTransService;
@PostMapping("/demo")
@Transactional(propagation = Propagation.REQUIRED)
public void demo()throws Exception{
testTransService.saveParent();
testTransService.saveChildren();
}
}
demo()方法上面加@Transactional(propagation = Propagation.REQUIRED)
saveChildren()方法上面加@Transactional(propagation = Propagation.REQUIRES_NEW)
调用postman运行
报错:java.lang.NullPointerException: null
结论:由于抛出空指针异常,所以saveChildren()这个新事物会回滚,而抛出的异常会传递给父方法,父方法的事务也会回滚,所有没有一条记录
另外一种情况
saveChildren()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren(){
saveChild1();
/*String demon=null;
System.out.println(demon.charAt(0));*/
saveChild2();
}
demo()
@PostMapping("/demo")
@Transactional(propagation = Propagation.REQUIRED)
public void demo(){
testTransService.saveParent();
testTransService.saveChildren();
String demon=null;
System.out.println(demon.charAt(0));
}
调用postman
报错:java.lang.NullPointerException: null
结论:可以证明required_new创建的时新事务
================================================================================================
演示nested
saveChildren
@Transactional(propagation = Propagation.NESTED)
public void saveChildren(){
saveChild1();
saveChild2();
}
demo
@PostMapping("/demo")
@Transactional(propagation = Propagation.REQUIRED)
public void demo(){
testTransService.saveParent();
testTransService.saveChildren();
String demon=null;
System.out.println(demon.charAt(0));
}
调用postman
报错是一定的:java.lang.NullPointerException: null
结论:父事务回滚,子事务也一起回滚
另外一种情况
demo
@PostMapping("/demo")
@Transactional(propagation = Propagation.REQUIRED)
public void demo(){
testTransService.saveParent();
testTransService.saveChildren();
}
saveChildren
@Transactional(propagation = Propagation.NESTED)
public void saveChildren(){
saveChild1();
String demon=null;
System.out.println(demon.charAt(0));
saveChild2();
}
万年不变报错:java.lang.NullPointerException: null
结论:子事务异常,父事务没有捕获异常,则一起回滚
另外一种情况
saveChildren
@Transactional(propagation = Propagation.NESTED)
public void saveChildren(){
saveChild1();
String demon=null;
System.out.println(demon.charAt(0));
saveChild2();
}
demo
@PostMapping("/demo")
@Transactional(propagation = Propagation.REQUIRED)
public void demo(){
testTransService.saveParent();
try {
//相当于数据库中的savepoint 捕获异常之后,不影响其他事务操作
testTransService.saveChildren();
} catch (Exception e) {
e.printStackTrace();
}
}
调用postman:
报错:java.lang.NullPointerException
结论:捕获子事务异常之后,父事务不需要回滚,子事务进行回滚