Spring 的事务传播行为
TransactionDefinition 中定义了 7 种类型的事务传播行为,是 Spring 框架独有的事务增强特性,不属于的事务实际提供方数据库行为。
REQUIRED : 如果当前没有事务,就新建一个事务,如果已经存在,则加入已经存在的事务
REQUIRES_NEW :新建事务,如果当前已经有事务就挂起当前事务
SUPPORTS :支持当前事务,如果没有事务就已非事务方式运行
NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,就挂起
MANDATORY : 使用当前事务,如果没有事务就抛出异常
NEVER : 非事务方式运行,当前存在事务抛出异常
NESTED :当前存在事务,则嵌套事务运行,不存在则采用和REQUIRED 类似的操作
关于MyBatis的介绍
关于MyBatis-Plus 的介绍
使用MyBatis构建工程进行测试
工程的gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
compile 'com.baomidou:mybatis-plus-boot-starter:3.4.1'
}
application.properties
server.port=8081
spring.datasource.password=xxxxx
spring.datasource.url=jdbc:mysql:/xx.xx.xx.xx:3306/mybatis_demo?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=xxxx
logging.level.com.study.mybatisplusdemo.user.mapper=debug
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@MapperScan("com.study.mybatisplusdemo")
public class MybatisPlusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemoApplication.class, args);
}
@Bean//分页插件
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("m_user")
public class User {
@TableId(value = "id",type= IdType.AUTO)
private Integer id;
@TableField("name")
private String name;
@TableField("age")
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.study.mybatisplusdemo.user.model.User;
import org.apache.ibatis.annotations.Select;
public interface UserMapper extends BaseMapper<User> {
@Select("select * from m_user where id=#{id}")
public User findUser(int id);
}
import com.study.mybatisplusdemo.user.mapper.UserMapper;
import com.study.mybatisplusdemo.user.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
//事务练习
public class TransactionService {
@Autowired(required = false)
private UserMapper userMapper;
//propagation= Propagation.REQUIRED 默认 ,一般RuntimeException会回滚
@Transactional
public User addRequired(User user){
userMapper.insert(user);
System.out.println(user);
return user;
}
//By default, a transaction will be rolling back on {@link RuntimeException}
// and {@link Error} but not on checked exceptions (business exceptions)
@Transactional
public User addRequiredException(User user){
userMapper.insert(user);
System.out.println(user);
throw new RuntimeException();
// return user;
}
@Transactional(propagation= Propagation.REQUIRES_NEW)
public User addRequiresNew(User user){
userMapper.insert(user);
System.out.println(user);
return user;
}
@Transactional(propagation= Propagation.REQUIRES_NEW)
public User addRequiresNewException(User user){
userMapper.insert(user);
System.out.println(user);
throw new RuntimeException();
// return user;
}
@Transactional(propagation= Propagation.NESTED)
public User addNested(User user){
userMapper.insert(user);
System.out.println(user);
return user;
}
@Transactional(propagation= Propagation.NESTED)
public User addNestedException(User user){
userMapper.insert(user);
System.out.println(user);
throw new RuntimeException();
}
}
主要测试的类 后面简单说明一下请求的结果
import com.study.mybatisplusdemo.user.model.User;
import com.study.mybatisplusdemo.user.service.TransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RequestMapping("/tran")
@RestController
public class TransactionController {
@Autowired
private TransactionService transactionService;
@Transactional
@RequestMapping("/req_exc_nc")
public List<User> requiredExceptionNotCatch(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequired(user1));
userList.add(transactionService.addRequiredException(user2));
return userList;
}
@Transactional
@RequestMapping("/req_exc_c")
public List<User> requiredExceptionCatch(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequired(user1));
try {
userList.add(transactionService.addRequiredException(user2));
}catch (Exception e){
e.printStackTrace();
}
return userList;
}
@Transactional
@RequestMapping("/reqn_exc_nc")
public List<User> requiredNewExceptionNotCatch(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequired(user1));
userList.add(transactionService.addRequiresNewException(user2));
return userList;
}
@Transactional
@RequestMapping("/reqn_exc_nc2")
public List<User> requiredNewExceptionNotCatch2(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequiresNew(user1));
userList.add(transactionService.addRequiresNewException(user2));
return userList;
}
@Transactional
@RequestMapping("/reqn_exc_c")
public List<User> requiredNewExceptionCatch(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequired(user1));
try {
userList.add(transactionService.addRequiresNewException(user2));
}catch (Exception e){
e.printStackTrace();
}
return userList;
}
@Transactional
@RequestMapping("/nest_exc_nc")
public List<User> nestedNewExceptionNotCatch(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequired(user1));
userList.add(transactionService.addNestedException(user2));
return userList;
}
@Transactional
@RequestMapping("/nest_exc_nc2")
public List<User> nestedNewExceptionNotCatch2(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addNested(user1));
userList.add(transactionService.addNestedException(user2));
return userList;
}
@Transactional
@RequestMapping("/nest_exc_c")
public List<User> nestedNewExceptionCatch(String n1, String n2){
User user1 = new User();
user1.setName(n1);
user1.setAge(11);
User user2 = new User();
user2.setName(n2);
user2.setAge(11);
List<User> userList = new ArrayList<>();
userList.add(transactionService.addRequired(user1));
try {
userList.add(transactionService.addNestedException(user2));
}catch (Exception e){
e.printStackTrace();
}
return userList;
}
}
下面就介绍其中几种事务的情况
注意不要在Service 事务方法里面使用this.事务方法,因为这样造成的结果可能和你预期的结果不一致,因为this不是被加强过的代理类
-
REQUIRED 默认的
requiredExceptionNotCatch 外围开启事务, 异常不catch
请求http://127.0.0.1:8081/tran/req_exc_nc?n1=aaa&n2=bbb
两个user 都插入失败,因为addRequired 和 addRequiredException 同属于一个事务,事务被回滚了requiredExceptionCatch 外围开启事务,异常catch
请求http://127.0.0.1:8081/tran/req_exc_nc?n1=aaa&n2=bbb
两个user 都插入失败,因为addRequired 和 addRequiredException 同属于一个事务,事务被回滚了,除非异常在事务方法内部被处理不被抛出
-
REQUIRES_NEW
requiredNewExceptionNotCatch 外围开启事务, 异常不catch
请求http://127.0.0.1:8081/tran/reqn_exc_nc?n1=aaa&n2=bbb
两个user 都插入失败,addRequired 和 addRequiresNewException 不属于同一个事务,事务异常被 抛出到最外层事务,事务被回滚了requiredNewExceptionCatch 外围开启事务, 异常catch
请求http://127.0.0.1:8081/tran/reqn_exc_c?n1=aaa&n2=bbb
addRequired插入成功 和 addRequiresNewException插入失败, 不属于同一个事务, addRequiresNewException 异常在外部被catch 没有抛出到最外层,所以addRequiresNewException 事务回滚,最外层事务不受影响requiredNewExceptionNotCatch2 外围开启事务, 异常不catch
请求http://127.0.0.1:8081/tran/reqn_exc_nc2?n1=aaa&n2=bbb
addRequiresNew插入成功 和 addRequiresNewException插入失败, 不属于同一个事务,事务异常被 抛出到最外层事务,事务被回滚了
-
NESTED
nestedNewExceptionNotCatch 外围开启事务, 异常不catch
请求 http://127.0.0.1:8081/tran/nest_exc_nc?n1=bbb&n2=ccc
两个user 都插入失败,addRequired 和 addNestedException不属于同一个事务,事务异常被抛出到最外层事务,事务被回滚了nestedNewExceptionCatch外围开启事务, 异常catch
请求 http://127.0.0.1:8081/tran/nest_exc_c?n1=bbb&n2=ccc
addRequired插入成功 和 addNestedException插入失败, 不属于同一个事务, addNestedException异常在外部被catch 没有抛出到最外层,所以addNestedException事务回滚,最外层事务不受影响nestedNewExceptionNotCatch2 外围开启事务, 异常不catch
请求 http://127.0.0.1:8081/tran/nest_exc_nc2?n1=eee&n2=fff
两个user 都插入失败,addNested和 addNestedException不属于同一个事务,事务异常被抛出到最外层事务,两个子事务都回滚了