Spring 的事务传播行为

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不是被加强过的代理类

  1. 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 同属于一个事务,事务被回滚了,除非异常在事务方法内部被处理不被抛出


  1. 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插入失败, 不属于同一个事务,事务异常被 抛出到最外层事务,事务被回滚了


  1. 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不属于同一个事务,事务异常被抛出到最外层事务,两个子事务都回滚了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值