首页 > 其他分享 >事务传播性——代码演示版超级详细

事务传播性——代码演示版超级详细

时间:2023-02-26 12:11:07浏览次数:33  
标签:代码 事务 Autowired transfer 超级 update jdbcTemplate 演示版 public

事务属性传播性

传播性 描述
REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务(默认)
SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起
NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起
NEVER 以非事务方式运行,如果当前存在事务,则抛出异常
NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED

解释:REQUIRED

第一种情况

第一个案例

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        //int i = 1/0;
    }
}

测试类

    @Test
    void contextLoads() {
        userService.transfer();
    }

控制台执行报错,数据回滚,是因为update方法在transfer方法内,而这个transfer方法出现了报错,而造成了回滚。

另外由REQUIRED 解释来看 update方法虽然有事务,但是他不是自己的事务,而是因为transfer自身有事务,update的事务就加入到transfer里面去,所以,transfer报错,都会回滚

另外下一个案例同理

同上面的代码transfer 中的int i = 1/0;方法注释掉,update()方法的int i = 1/0;开启,一样能造成回滚

第二案例

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        //int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        int i = 1/0;
    }
}
@RestController
public class HelloController {
    @Autowired
    UserService userService;
    @GetMapping("/hello")
    public void hello(){
        userService.transfer();
    }
}

用的是apifox来进行测试 http://localhost:8080/hello

image-20220807142117517.png

Creating new transaction with name [org.javaboy.spring_tran04.UserService.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

image-20220807142850278.png
创建名称为 [org.javaboy.spring_tran04.UserService.transfer] 的新事务:PROPAGATION_REQUIRED(事务),ISOLATION_DEFAULT(隔离)

这句话的意思就是说 只有org.javaboy.spring_tran04.UserService.transfer 开启了PROPAGATION_REQUIRED级别的事务 (transfer 方法)。另外ISOLATION_DEFAULT隔离级别也就是个默认的级别(也就是说,将 Spring 的事务隔离级别设置为 ISOLATION—DEF AULT 时, Spring 不做事务隔离级别的处理,会直接使用数据库默认的事务隔离级别。)

image-20220807143039630.png
Participating in existing transaction

意思就是transfer 已经开启了事务,而update方法就加入到这个事务中来了

第二种情况

第一个案例

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    //@Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        //int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        int i = 1/0;
    }
}
@RestController
public class HelloController {
    @Autowired
    UserService userService;
    @GetMapping("/hello")
    public void hello(){
        userService.transfer();
    }
}

依旧使用APIfox来发送请求 http://localhost:8080/hello

image-20220807143426048.png
此时数据就有了,但是只有一个生效了

此时的控制台打印

Creating new transaction with name [org.javaboy.spring_tran04.UserService2.update]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

意思是:只有UserService2中的update方法的事务开启了,默认的事务,默认的隔离级别

总结

如果一个方法开启了事务,内部的方法也开启了事务,那么内部的方法的事务是无效的,都由外部的事务说了算。

如果外部方法未开启事务,而内部的方法开启了事务,那么外部不受内部的事务控制

解释REQUIRES_NEW

跟REQUIRED不同的是:

REQUIRED如果内外部都有事务,那么内部的事务就不会开启,而都是跟着外部的事务走

REQUIRES_NEW如果内外部都有事务,可以理解为,创建了两个事务,内部的事务是一个独立的事务

先举个例子,A方法开启了事务,而内部有个方法B,B也开启了事务
如果A方法抛出了异常,并不一定会造成B也回滚了,因为B是一个独立的事务,
如果B也抛出了异常分至于会不会影响A回滚会有两种情况:首先B肯定会回滚
1.如果B抛出了异常,并且处理了,那么A就不会回滚,
2.如果B未处理异常,那么A就会回滚

测试之前,有个前提,就是给数据库的username字段加上索引

要不然会报出错误,锁等待超时

(就是在执行A的时候,整个表被锁住了,未提及事务,B因此会被锁住,就会报错,超时了)

解决的方式是

image-20220807151327737.png

第一种情况 内部方法不处理异常

第一个案例

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        //int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        //int i = 1/0;
    }
}
@RestController
public class HelloController {
    @Autowired
    UserService userService;
    @GetMapping("/hello")
    public void hello(){
        userService.transfer();
    }
}

结果money都变成了1000!

image-20220807152705151.png
第二个案例

首先把数据库里面的zhangsan 和lisi 的Money 都设置为1;

只改变UserService的代码

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        int i = 1/0;
    }

}

数据库发生了改变

image-20220807153215446.png
第三个案例

首先把数据库里面的zhangsan 和lisi 的Money 都设置为1;

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        //int i = 1/0;
    }

}

@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        int i = 1/0;
    }
}

数据都会回滚

数据库里面的zhangsan 和lisi 的Money 都为1;

第二种情况 内部方法处理了异常

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        try {
            userService2.update();
        }catch (Exception e){
            //e.printStackTrace();
        }
        //int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        int i = 1/0;
    }
}

数据库的money前两个都设置为1

当发送请求

image-20220807153841840.png
因为处理了异常,外部的不会滚,内部的会回滚

解释NESTED

NESTED 表示:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED。

第一个案例

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        userService2.update();
        int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional(propagation = Propagation.NESTED)
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        //int i = 1/0;
    }
}

结论:外部发生了异常,内部也会跟着回滚

image-20220807154707009.png

第二个案例

@Service
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    UserService2 userService2;
    @Transactional
    public void transfer(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"zhangsan");
        try {
            userService2.update();
        }catch (Exception e){
            e.printStackTrace();
        }
        //int i = 1/0;
    }

}
@Service
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional(propagation = Propagation.NESTED)
    public void update(){
        jdbcTemplate.update("update user1 set money = ? where username=?",1000,"lisi");
        int i = 1/0;
    }
}

结论:如果内部发生了异常,外部进行了一个处理,那么外部就不会回滚

image-20220807155036253.png

解释MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

就是说A有事务,B的事务是MANDATORY 那么B的事务就加入到A中。如果A没有事务,那么就报错了

解释SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

见名知意

解释NOT_SUPPORTED

以非事务方式运行,如果当前存在事务,则把当前事务挂起

SUPPORTS 表示如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

也就是说内部肯定是不会回滚的,就算内部报错了,内部也不会回滚,假如外部报错了,外部处理了异常,那么外部不会回滚,反之

解释NEVER

以非事务方式运行,如果当前存在事务,则抛出异常

回滚规则

默认情况下,事务只有遇到运行期异常(RuntimeException 的子类)以及 Error 时才会回滚,在遇到检查型(Checked Exception)异常时不会回滚。

像 1/0,空指针这些是 RuntimeException,而 IOException 则算是 Checked Exception,换言之,默认情况下,如果发生 IOException 并不会导致事务回滚。

如果我们希望发生 IOException 时也能触发事务回滚,那么可以按照如下方式配置:

@Transactional(rollbackFor = IOException.class)
public void handle2() {
    jdbcTemplate.update("update user set money = ? where username=?;", 1, "zhangsan");
    accountService.handle1();
}

标签:代码,事务,Autowired,transfer,超级,update,jdbcTemplate,演示版,public
From: https://www.cnblogs.com/newTuiMao/p/17156420.html

相关文章

  • babel对ES6的模块化代码转换
    1安装工具babel-clibabel-preset-envbrowserify(webpack).babelrc配置{presets":["babel-preset-env"]2npxbabelsrc/js-ddist/js3打包npxbrowserifydist/js/app......
  • python计算Friedman排名代码
    python计算Friedman排名代码首先先说输入数据,为了迅速处理,采用csv格式的表格,读者可以先理解这里提供的示例和代码,再自行调整下面是代码,代码会生成一个排名文件Rank.c......
  • 【C语言】通讯录《静态内存版本》完整代码
    ......
  • 动态数组代码实现
    一、动态数组一般在静态数组定义后,系统就会为其分配对应长度的连续的专有内存空间,可是,我们都知道,不同的运行样例,所需要的数组长度是不一样的,为了所有样例都可以执行,一般......
  • 代码随想录算法训练营Day24 回溯算法|216.组合总和III 17.电话号码的字母组合
    代码随想录算法训练营216.组合总和III题目链接:216.组合总和III找出所有相加之和为 n的 k 个数的组合。组合中只允许含有1- 9的正整数,并且每种组合中不存在重复......
  • github创建代码储存库,上传代码
    Github说明--如何在Github里面上传自己的代码-yesyes1-博客园(cnblogs.com)我详细看了lzj同学的这篇博客,简单介绍一下,如何上传代码存储到代码库。1.点击头像傍边的......
  • Java中的代码块
    Java中的代码块我们在做笔试题的时候经常会遇到考察类中代码块运行顺序的题,所以我现在把它总结一下。在Java中,代码块(Block)是用一对大括号括起来的一段代码,它可以包含多条......
  • 关于低代码和无代码---喧嚣背后的致命问题
    前言2021年的时候,刮起了一阵”低代码”和”无代码”的风,结果猪没见吹起来,风却早早停了。在我的职业生涯中遇到了很多的低代码的构想和实现,通常他们的想法非常朴素:写代码......
  • web前端开发第202页习题3代码
    排序参考1<!DOCTYPEhtml>2<htmllang="en">34<head>5<metacharset="UTF-8">6<metahttp-equiv="X-UA-Compatible"content="IE=edge">7......
  • 1688店铺所有商品接口(整店商品数据接口)采集代码分享
    背景大家有探讨稳定获取1688店铺整店商品列表主图、价格、标题,及sku的完整解决方案。这个引起了我技术挑战的兴趣。目前,自己做了压测,QPS高、出滑块概率极低,API整体稳定,可满......