spring事物使用示例及原理总结

news/2024/12/4 3:55:02/

目录

事务示例

示例一

示例二

示例三

示例四

示例五

示例六

事务原理 

@EnableTransactionManagement

执行代理对象目标方法


事务示例

示例一

在test()方法直接调用abc()方法,并在test()方法添加@Transactional,test()和abc()方法分别会更新id=1和id=2的数据name字段,abc()方法中会抛出异常。

@Component
public class TransactionService {@Autowiredprivate TransactionService service;@Resourceprivate UserMapper userMapper;@Transactional(rollbackFor = Exception.class)public String test(String name, String target) throws Exception {User user = new User();user.setId(1l);user.setName(name);userMapper.updateById(user);abc(target);return "ok";}public void abc(String target) throws Exception {User user = new User();user.setId(2l);user.setName(target);userMapper.updateById(user);throw new Exception("error");}}

 使用TransactionController对外提供接口做测试

@RestController
public class TransactionController {@Autowiredprivate TransactionService service;@GetMapping("testT")public String test(@RequestBody Map<String, Object> map) throws Exception {return service.test(map.get("name").toString(), map.get("target").toString());}}

测试之前先看数据库的值:

执行  http://localhost:8080/testT 测试

 不难看出,两条更新操作都会被回滚。

示例二

在abc()方法中添加@Transactional(rollbackFor = Exception.class)注解

    @Transactional(rollbackFor = Exception.class)public void abc(String target) throws Exception {User user = new User();user.setId(2l);user.setName(target);userMapper.updateById(user);throw new Exception("error");}

结果还是一样,都会被回滚。因为test()方法是调用的TransactionService原始对象的abc()方法。

示例三

在TransactionService中新增TransactionService 类型的成员属性,通过@Autowired注入spring bean对象,并在test()方法中使用service属性调用abc(),其用意是调用spring创建的代理对象的abc()方法

最终结果还是一样都会回滚,因为这个线程只会创建一个事务,同一个事务要么全部回滚,要么全部提交。

示例四

在abc()方法的注解改为@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW),其用意是同一个线程在执行test()方法时创建一个事务,在调用service.abc()方法时又会创建一个事务。

最终两个方法执行的数据库更新操作还是会回滚,因为线程在执行abc()方法后抛出的异常,在test()方法中并没有处理这个异常,所以两个事务都会回滚。

示例五

在test()方法中将service.abc()方法用try catch捕获异常。

再次执行,就会发现test()方法事务会提交,而abc()方法事务会回滚。如图,id=1的name属性修改james,id=2的name属性没有发生变化。

示例六

在test()方法中将service.abc()方法try catch捕获,并且abc()方法使用的事务和test()方法使用同一个事务,此时事务会提交还是回滚呢?还会出现和示例五中abc()方法更新操作回滚,test()方法更新操作提交的效果呢?

思考一会。。。

首先这两个方法使用的是同一个事务,故它们要么全部提交,要么全部回滚。那到底是回滚还是提交,将由跟spring事务管理器中的一个属性相关,该属性为globalRollbackOnParticipationFailure,它默认等于true,表示如果参与的事务有失败的,则标记为为回滚。如果将他设置为false,表示局部执行失败的事务,也会标记为提交。所以这个事务还是会回滚。

如果将事务管理器的globalRollbackOnParticipationFailure设置为false,那么该事务就会提交。

小结:在a()方法调用b()方法时,如果他们使用同一个事务,他们的数据库更新操作要么全部提交,要么全部回滚;如果b()方法事务传播属性为REQUIRES_NEW,表示在执行b()方法时创建新事物,此时a()方法事务的提交和回滚可以不受b()方法的事务控制。 

事务原理 

@EnableTransactionManagement

spring boot启动添加@EnableTransactionManagement注解,表示开启事务功能。spring解析@EnableTransactionManagement注解流程如下图。

a、导入TransactionManagementConfigurationSelector类,执行selectImports()方法,加载AutoProxyRegistrar和ProxyTransactionManagementConfiguration类。

b、spring在解析AutoProxyRegistrar时执行registerBeanDefinitions()方法,它会将InfrastructureAdvisorAutoProxyCreator注册到spring容器中,InfrastructureAdvisorAutoProxyCreator是一个BeanPostProccessor,在其父类中实现了postProcessBeforeInstantiation()方法,在实例化前会给目标对象创建代理对象。

c、spring在解析ProxyTransactionManagementConfiguration时分别会创建3个bean对象,BeanFactoryTransactionAttributeSourceAdvisor、TransactionInterceptor和AnnotationTransactionAttributeSource,分别是advisor、advice和pointcut,advice是代理逻辑,pointcut是切入点,advisor可以理解为是advice+pointcut组成的。

执行代理对象目标方法

a、当调用被代理对象目标方法时,会进入TransactionInterceptor#invoke()方法,执行invokeWithinTransaction()方法

b、getTransaction()中创建事务对象,如果事务传播属性为REQUIRED,REQUIRES_NEW,NESTED时,执行startTransaction()开启并设置事务属性。

c、首先从事务对象中获取连接对象,将连接对象设置为不自动提交。

d、执行prepareTransactionInfo()方法创建TransactionInfo对象,并将getTransaction()返回的TransactionStatus对象设置到TransactionInfo对象,然后调用bindToThread()方法获取当前线程ThreadLocal对象中的TransactionInfo对象,将其赋值给oldTransactionInfo缓存起来,将新创建的TransactionInfo对象设置到ThreadLocal对象中去。

d、执行目标方法

f、提交或者回滚,如果目标方法执行失败,抛出异常,则会执行completeTransactionAfterThrowing()进行回滚,回滚之后执行finally块的cleanupTransactionInfo()方法,将缓存在oldTransactionInfo属性的TransactionInfo对象重新设置到当前线程的ThreadLocal对象中去;如果目标方法执行成功,则先执行cleanupTransactionInfo()方法,再执行commitTransactionAfterReturning()方法提交事务。

流程图


http://www.ppmy.cn/news/6671.html

相关文章

《计算机软件基础》实验一

实验一&#xff1a;线性表&#xff0f;栈与队 一、实验目的 了解实现线性表&#xff0f;栈与队&#xff0f;数组所涉及到的数据结构。 二、实验要求 因受时间限制&#xff0c;从以下给出的4个实验内容中任选3个&#xff0c;分析并补齐给出的源程序&#xff0c;运行相应的程…

20221226今天的世界发生了什么

///南山控股&#xff1a;控股子公司拟设合伙企业投资仓储物流领域 合伙企业专注于仓储物流领域&#xff0c;主要对重要城市区域的仓储物流类的商业不动产进行投资。目前计划投资南京、嘉兴、昆明等地的仓储物流类项目 ///岭南股份&#xff1a;子公司与腾讯授权集成商盛灿科技签…

再学C语言14:基本运算符

C使用运算符&#xff08;operator&#xff09;代表算数运算 一、赋值运算符&#xff08;assignment operator&#xff09;&#xff1a; 在C中&#xff0c;符号并不表示“相等”&#xff0c;而是一个赋值运算符 year 2022; 符号左边是一个变量名&#xff0c;右边是赋给该变…

K. The Robot(思维 + 看数据范围)

Problem - 1468K - Codeforces 有一个机器人在一个格子场上&#xff0c;这个格子场在各个方向都是无尽的。最初&#xff0c;机器人位于坐标为(0,0)的单元中。他将执行由一串大写拉丁字母 "L"、"R"、"D"、"U "描述的命令。当一个命令被…

device_create() 创建设备节点,device_del()删除设备节点

0 背景 最近在尝试开发linux设备驱动&#xff0c;虽然可以在命令行下通过mknod命令手动创建设备节点&#xff0c;但是这种操作不符合标准驱动的开发过程&#xff0c;并且linux内核提供了一组函数函数device_create() 和device_del()可以在加载驱动时候自动在/dev目录下创建相应…

十、Express 路由

路由是Express框架中最重要的功能之一&#xff0c;在Express中&#xff0c;路由指的是客户端的请求与服务器处理函数之间的映射关系&#xff0c;Express中的路由分别由请求的类型&#xff08;GET/POST等&#xff09;、请求的URL地址和处理函数三个部分组成的&#xff1b; APP级…

QT入门-UI-信号槽

目录 一、QWidget类&#xff08;重点&#xff09; 二、子组件&#xff08;掌握&#xff09; 三、样式表&#xff08;熟悉&#xff09; 一、什么是信号槽&#xff1f; 二、信号槽的连接方式 2.1 自带信号→自带槽 2.2 自带信号→自定义槽 2.3 自定义信号 三、传参方式 3.1 成员变…

IT咨询——怎样保障企业的数字化转型

数字化转型不是个新命题了&#xff0c;很多企业都在搞&#xff0c;有成功的也有失败的&#xff0c;数字化转型走到今天&#xff0c;业界已经形成一些共识&#xff0c;比如&#xff1a; 1、数字化转型是一把手工程&#xff0c;是决策层最重要的工作之一&#xff0c;需要作为企业…