事务@Transactional详解

4 篇文章 0 订阅
3 篇文章 0 订阅

事务@Transactional

前言:相关jar
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

根据@Transactional(propagation= Propagation.NOT_SUPPORTED)讲解

一.源码

package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";
	//默认
    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

在这里插入图片描述

可以看到什么都不写的时候默认Propagation.REQUIRED,是指当前的事务一定要使用一个事务的,就是当前的事务不存在,则会创建一个新的事务,如果存在事务,则会加入到当前事务中

二.点进去Propagation

package org.springframework.transaction.annotation;

public enum Propagation {
    REQUIRED(0), //使用当前事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的,如果当前存在事务,则加入这个事务,成为一个整体
    SUPPORTS(1),//如果当前有事务,则使用事务,如果当前没有事务,则不使用事务
    MANDATORY(2),//该属性强制必须存在一个事务,如果当前没有事务,就抛出异常
    REQUIRES_NEW(3),//如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;如果没有事务,同REQUIRED
    NOT_SUPPORTED(4),//以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 
    NEVER(5),//如果当前存在事务,则抛出异常
    NESTED(6);/*如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者 回滚;如果当前没有事务,则同REQUIRED,但是如果主事务提交,则会携带子事务一* 起提交。
     *  如果主事务回滚,则子事务一起回滚,相反,子事务异常,则父事务可以混滚或者不回滚。*/

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

ctrl+f12可以调出当前类的所有方法以列表的方式展示出来

三.事务不生效的的几种方法

1.本类方法调用,假如有一个类Test,有一个方法A,方法A调用方法B(不论方法B是用public还是private修饰,此时方法B声明事务,但是方法A没声明事务,外部方法调用方法A时,方法B的事务是不会起作用的

如:

    //11  在service写下如下方法
    @Override
    // @Transactional(propagation = Propagation.REQUIRED)
    public void testPropagation() {
        saveParent();
        saveChildren();
    }
	//22
    public void saveParent() {
        User user = new User();
        user.setName("张三");
        user.setAge(00);
        user.setEmail("123456@qq.com");
        userMapper.insert(user);
    }
	//33
    // @Transactional(propagation = Propagation.REQUIRED)
    public void saveChildren(){
        saveChild1();
        //此处留一处异常便于测试
        int a = 1/0;
        saveChild2();
    }
    //44
    public void saveChild1(){
        User user = new User();
        user.setName("child-1");
        user.setAge(11);
        user.setEmail("123456@qq.com");
        userMapper.insert(user);
    }
    //55
    public void saveChild2(){
        User user = new User();
        user.setName("child-2");
        user.setAge(22);
        user.setEmail("654321@qq.com");
        userMapper.insert(user);
    }
//然后test里面新建一个test
package com.test;

import com.river.Application;
import com.river.UserService;
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 BasisTest {

    @Autowired
    private UserService userService;
    @Test
    public void testPro(){
        userService.testPropagation();
        System.err.println("运行成功");
    }
}

①:把所有的事务都注释掉,放开33的事务 里面有这么一段话@Transactional(propagation = Propagation.REQUIRED)

可以看到本类方法11调用本类方法22和33,在22或者33上加上的事务是不会起作用的

2.异常被你的 catch“吃了”导致@Transactional失效

3.数据库引擎不支持事务,例如myisam


四.事务使用的七种方式

下面贴出三段代码

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
	
    @Autowired
    private UserMapper userMapper;
    
    //11
    @Override
    public void saveParent() {
        User user = new User();
        user.setName("张三");
        user.setAge(0);
        user.setEmail("123456@qq.com");
        userMapper.insert(user);
    }
    //22
    @Override
    //@Transactional(propagation = Propagation.REQUIRED)
    public void saveChildren(){
        saveChild1();
        int a = 1/0;//此处异常,因为0不能当除数
        saveChild2();
    }
    //33
    public void saveChild1(){
        User user = new User();
        user.setName("child-1");
        user.setAge(11);
        user.setEmail("123456@qq.com");
        userMapper.insert(user);
    }
    //44
    public void saveChild2(){
        User user = new User();
        user.setName("child-2");
        user.setAge(22);
        user.setEmail("654321@qq.com");
        userMapper.insert(user);
    }
}

TruckServiceImpl

@Service
public class TruckServiceImpl implements TruckService {
    @Autowired
    private UserService userService;
	
	//55
    @Override
    //@Transactional(propagation = Propagation.REQUIRED)
    public void testPro() {
        userService.saveParent();
        userService.saveChildren();
       //int a = 1/0;//此处异常,因为0不能当除数
    }
}

BasisTest

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class BasisTest {

    @Autowired
    private TruckService truckService;
    @Test
    public void testPro(){
        truckService.testPro();
        System.err.println("运行成功");
    }
}
1.事务的传播:Propagation.REQUIRED(多用于增,删,改,什么都不写的时候是默认是它)
①:把所有的事务都注释掉,运行BasisTest

结果:

在这里插入图片描述

可以看到:22的方法中有一处明显的异常,所以是执行不下去的,在异常之前的数据都已经执行,异常如下
在这里插入图片描述

结论:当方法中没有事务的时候,异常之前的方法会执行,异常之后的方法不执行

②:把父类方法55的@Transactional(propagation = Propagation.REQUIRED)注释放开

可以看到,什么数据也没有
在这里插入图片描述

结论:事务进行了传播,父方法的事务会传递到子方法中,当子方法中遇到异常,事务都会回滚

③:父方法事务注释,把子方法中的带注释为22的事务@Transactional(propagation = Propagation.REQUIRED)放开

父级方法中没有事务,而子级方法中有事务,可以看到
在这里插入图片描述

不带有事务方法的数据进行了插入,而带有事务的方法遇到了异常进行了回滚

结论:如果一个方法带有事务,内部调用的方法出现了异常,它和它的子方法都会进行回滚;这个方法之前的方法不带有事务的话,正常运行,不会回滚

④:把22和55的@Transactional(propagation = Propagation.REQUIRED)都放开

可以看到,什么数据都不会有
在这里插入图片描述

父方法和子方法都有事务,并且数据也进行了回滚

结论:如果当前存在事务,则加入这个事务,成为一个整体

2.事务的的支持Propagation.SUPPORTS(多用于查询)
①:注释掉所有事务,把22的事务换成@Transactional(propagation = Propagation.SUPPORTS)

可以看到,异常之前的方法都已经执行
在这里插入图片描述
结论:如果父方法中没有事务,就以没有事务的方式运行

②:父方法中加上@Transactional(propagation = Propagation.REQUIRED),子方法加上@Transactional(propagation = Propagation.SUPPORTS)

可以看到
在这里插入图片描述

结论:如果当前存在事务,就以事务的方式运行,如果不存在事务,就以非事务的方式运行

3.事务的强制Propagation.MANDATORY

类似于EJB的事务属性

①:注释掉所有事务,在子方法注释为22中增加事务@Transactional(propagation = Propagation.MANDATORY)

可以看到如下异常
在这里插入图片描述

结论:使用Propagation.MANDATORY时,父方法必须有事务,如果没有事务,则会抛出异常

②:父方法55加上事务@Transactional(propagation = Propagation.REQUIRED),子方法22加上事务@Transactional(propagation = Propagation.MANDATORY)

可以看到,数据进行了回滚
在这里插入图片描述

结论:MANDATORY必须有父事务,没有的话抛出异常

4.事务的挂起@Transactional(propagation = Propagation.REQUIRES_NEW)
①:注释掉所有事务,在22的子方法中加上事务@Transactional(propagation = Propagation.REQUIRES_NEW)

可以看到:父方法中没有加事务的的方法运行了,子方法加事务的,有异常就回滚了
在这里插入图片描述

结论:REQUIRES_NEW会创建一个新事务

②:放开55的事务注释@Transactional(propagation = Propagation.REQUIRED),和22事务共用

在这里插入图片描述

父方法一个事务,子方法新创建一个事务,当子方法遇到异常时,会回滚,回滚以后本身会有一个异常,所以父方法也会回滚,就会影响到父方法,导致没有数据

结论:当父方法有事务时,子方法REQUIRES_NEW会新创建一个事务,当子方法的事务遇到异常时,父方法的也会进行回滚

③:把22的异常注释掉,加入到父方法的末尾中,如55的注释位置

在这里插入图片描述

可以看到,父方法中的没加REQUIRES_NEW事务的进行了回滚,而加REQUIRES_NEW则是创建了一个新事务进行了数据操作

结论:如果存在事务,则创建一个新的事务单独执行,如果不存在,创建一个新事务

5.事务的挂起Propagation.NOT_SUPPORTED

把55的主方法的异常删除掉,放开子方法22的异常

①:在子方法中注释为22的上面加上注解@Transactional(propagation = Propagation.NOT_SUPPORTED)

可以看到
在这里插入图片描述

结论:在子方法中调用并没有出现事务

②:在父方法55中加上@Transactional(propagation = Propagation.REQUIRED),子方法中22中加入@Transactional(propagation = Propagation.NOT_SUPPORTED)

可以看到,主方法的数据没有保存,子方法的异常之前的都保存了
在这里插入图片描述

子方法抛异常没有回滚,主方法却回滚了

结论:如果当前有事务,则把事务挂起,自己不使用事务进行操作

6.事务的传播@Transactional(propagation = Propagation.NEVER)
①:主方法事务注释放开,子方法加上 Propagation.NEVER,会报错

在这里插入图片描述

有事务则会报异常

注释掉主方法的事务
在这里插入图片描述

可以看到,异常之前的方法都插入了数据库

结论:子方法如果加入@Transactional(propagation = Propagation.NEVER),则主方法不允许加事务

7.事务的传播@Transactional(propagation = Propagation.NESTED)
①:子方法22加上事务@Transactional(propagation = Propagation.NESTED),注释掉异常,主方法55放开注释,加上事务,并且在末尾加上异常

在这里插入图片描述

可以看到,数据库中没有数据,事务被回滚

结论:当子方法加上@Transactional(propagation = Propagation.NESTED)时,如果主方法有异常,则会回滚

②:主方法55事务注释放开,子方法加入事务@Transactional(propagation = Propagation.NESTED)子方法中加入try …catch,主方法异常注释,子方法异常放开

在这里插入图片描述

可以看到,异常由于被捕捉,子方法异常之前的方法被执行

结论:当子方法的异常被try … catch时,事务会在异常之前运行,且不会回滚

③:注释掉主方法的事务,只留子方法的事务@Transactional(propagation = Propagation.NESTED)

②:如果主方法没有事务,则同于@Transactional(propagation = Propagation.REQUIRED)

可以看到
在这里插入图片描述

作用同REQUIRED一样

结论:当主方法没有事务的时候,子方法加入注解@Transactional(propagation = Propagation.REQUIRED),主方法的语句插入数据库,子方法中有异常则回滚。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值