网站模块建设,自己想建设一个网站,辽宁城乡建设厅网站,电子商务公司网站怎么建文章目录1. 事务的定义2. Spring 中事务的实现2.1 MySQL 中使用事务2.2 Spring 中编程式事务的实现2.3 Spring 中声明式事务2.3.1 声明式事务的实现 Transactional2.3.2 Transactional 作用域2.3.3Transactional 参数设置2.3.4 Transactional 异常情况2.3.5 Transactional 工作…
文章目录1. 事务的定义2. Spring 中事务的实现2.1 MySQL 中使用事务2.2 Spring 中编程式事务的实现2.3 Spring 中声明式事务2.3.1 声明式事务的实现 Transactional2.3.2 Transactional 作用域2.3.3Transactional 参数设置2.3.4 Transactional 异常情况2.3.5 Transactional 工作原理3. 事务隔离级别3.1 事务特性3.2 Spring 中设置事务隔离级别4. Spring 事务传播机制4.1 事务传播机制是什么4.2 为什么需要事务传播机制4.3 事务传播机制有哪些4.4 Spring 事务传播机制使用4.4.1 支持当前事务REQUIRED 默认4.4.2 不支持当前事务REQUIRES_NEW4.4.3 不支持当前事务NOT_SUPPORTED4.4.4 NESTED 嵌套事务4.4.5 嵌套事务和加入事务的区别本篇重点总结 在 Spring 项目中使用事务有两种方式编程式手动操作和声明式自动提交声明式自动提交使用最多只需要在方法上添加注解 Transactional设置事务的隔离级别 Transactional(isolation Isolation.SERIALIZABLE)Spring 中的事务隔离级别有5种设置事务的传播机制 Transactional(propagation Propagation.REQUIRED)Spring 中的事务传播级别有 7 种 1. 事务的定义
事务定义将一组操作封装成一个执行单元封装到一起要么全部成功要么全部失败
那么为什么要用事务呢
比如两个银行账户之间的转账操作
第一步操作A 账户 -100 元第二步操作B 账户 100 元
如果没有事务。第一步执行成功了第二步执行失败了那么 A 账号就丢失了 100 元而如果使用事务就可以解决这个问题让这一组操作要么一起成功要么一起失败 2. Spring 中事务的实现
Sping 中事务的操作用两种
编程式事务手写代码操作事务声明式事务利用注解自动开启和提交事务
2.1 MySQL 中使用事务
MySQL 中事务有 3 个重要的操作开启事务、提交事务、回滚事务它们对应的操作命令如下
-- 开启事务
start transaction;-- 业务执⾏
-- 提交事务
commit;
-- 回滚事务
rollback;2.2 Spring 中编程式事务的实现
Spring 中手动操作事务和 MySQL操作事务类似也是有 3 个重要操作
开启事务获取事务提交事务回滚事务
Spring Boot 内置了两个对象DataSourceTransactionManager 事务管理器用来获取事务开启事务、提交或回滚事务的而 TransactionDefinition 是事务的属性在获取事务的时候需要将 TransactionDefinition 传递进去从而获得一个事务 TransactionStatus 对象
RestController
public class UserController {Autowiredprivate UserService userService;Autowiredprivate DataSourceTransactionManager transactionManager;Autowiredprivate TransactionDefinition transactionDefinition;// 在此方法中使用编程式的事物RequestMapping(/add)public int add(UserInfo userInfo) {// 非空效验【验证用户名和密码不为空】if(userInfonull || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 开启事务获取事务TransactionStatus transactionStatus transactionManager.getTransaction(transactionDefinition);int result userService.add(userInfo);System.out.println(add 受影响的行数 result);// 提交事务transactionManager.commit(transactionStatus);
// // 回滚事务
// transactionManager.rollback(transactionStatus);return result;}
}
运行程序分别查看提交事务和回滚事务的效果 2.3 Spring 中声明式事务
2.3.1 声明式事务的实现 Transactional
声明式事务的实现只需要在方法上添加 Transactional 注解就可以实现无序手动开启事务和提交事务进入方法时自动开启事务方法执行完全会自动提交事务如果中途发生了没有处理的异常会自动回滚事务
// 在此方法中使用声明式的事物
// 在进入方法之前自动开启事务在方法之前完后自动提交事务如果出现异常则自动回滚事务
Transactional
RequestMapping(/add2)
public int add2(UserInfo userInfo) {if(userInfonull || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result userService.add(userInfo);System.out.println(add2 受影响的行数 result);int num 10/0;return result;
}去除异常的那行代码重新运行程序 2.3.2 Transactional 作用域
Transactional 可以用来修饰方法或类
修饰方法时只能应用到 public 方法上否则不生效修饰类时说明该注解对该类中所有的 public 方法都生效 2.3.3Transactional 参数设置
参数作用value当你配置多个事务管理器时可以使用该属性指定选择用哪个事务管理器transactionManager同上propagation事务的传播行为默认值为 Propagation.REQUIREDisolation事务的隔离级别默认值为 Isolation.DEFAULTtimeout事务的超时时间默认值为-1如果超过该时间限制但事务还没完成则自动回滚事务readOnly指定事务是否为只读事务默认值为 false为了忽略那些不需要事务的方法比如读取数据可以设置 read-only 为 truerolibackFor用于指定能够触发事务回滚的异常类型可以指定多个异常类型rolibackForClassName同上noRolibackFor抛出指定的异常类型不回滚事务也可以指定多个异常类型noRollbackForClassName同上
设置事务的隔离级别 设置事务的超时时间 2.3.4 Transactional 异常情况
Transactional 在异常被捕获的情况下不会进⾏事务⾃动回滚
Transactional
RequestMapping(/add3)
public int add3(UserInfo userInfo) {if(userInfonull || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result userService.add(userInfo);System.out.println(add2 受影响的行数 result);try {int num 10/0;} catch (Exception e) {}return result;
}解决方法1将异常重新抛出去
对于捕获的异常事务是会⾃动回滚的因此解决⽅案1就是将异常重新抛出 解决方法2使用代码的方式手动回滚当前事务
手动回滚事务在⽅法中使⽤ TransactionAspectSupport.currentTransactionStatus() 可以得到当前的事务然后设置回滚方法 setRollbackOnly 就可以实现回滚了具体实现代码 TransactionalRequestMapping(/add3)public int add3(UserInfo userInfo) {if(userInfonull || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result userService.add(userInfo);System.out.println(add2 受影响的行数 result);try {int num 10/0;} catch (Exception e) {//手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}2.3.5 Transactional 工作原理
Transactional 是基于 AOP 实现的AOP 又是使用动态代理来实现的。如果目标对象实现了接口默认情况下采用 JDK 的动态代理如果目标对象没有实现接口会使用 CGLIB 动态代理。Transactional 在开始执行业务之前通过代理先开启事务在执行成功之后再提交事务如果中途遇到异常则回滚事务
Transactional 实现思路 Transactional 具体执行细节 3. 事务隔离级别
3.1 事务特性
事务有四大特性ACID原子性、持久性、一致性和隔离性
原子性Atomicity一个事务中的所有操作要么全部完成要么全部不完成。不会结束在中间某个环节。事务在执行过程中发生错误会被回滚到事务开始前的状态一致性Consistency在事务开始之前和事务事务结束之后数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则这包含资料的精准度、串联性以及后续数据库可以自发性地完成预定的工作持久性Isolation事务处理结束后对数据的修改就是永久的。即使系统故障也不会丢失隔离性Durability数据库允许多个并发事务同时对其数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由交叉执行而导致数据的不一致。
这四个特性中只有隔离性隔离级别是可以设置的 3.2 Spring 中设置事务隔离级别
为什么要设置事务的隔离级别
设置事务的隔离级别是用来保障多个并发事务执行更可控更符合操作者预期的
这个可控表示的是比如疫情的时候有确诊、密接、次密接等针对不同的人群采取不同的隔离级别这种方式与事务的隔离级别类似都是让某种行为操作变的 更可控事务的隔离级别就是为了防止其他事务影响当前事务执行的一种策略
MySQL 事务隔离级别有 4 种
读未提交READ UNCOMMITTED 事务A读到了事务B没有提交的数据然后过了一会事务B进行了回滚此时**事务A的这个情况就叫脏读读到的数据也叫脏数据读未提交侧重于查询**既然有了脏读那么也肯定有不可重复读和幻读的问题读已提交READ COMMITTED针对上面脏读的问题来解决的事务A读到了事务B已经提交的数据然后过了一会事务B将提交的数据进行修改了此时事务A又读了一次B的数据发现两次读到读到数据不一样这个就叫不可重复读的问题读已提交侧重的是修改还是存在不可重复读和幻读的问题可重复读REPEATABLE READ 针对的是上面不可重复读的问题事务A此时查询数据发现表中只有一条数据然后事务B又过来拆台了事务B又插入了一条数据事务A再次查询发现哎我出现幻觉了吗刚刚不是只有一条数据么现在咋又变成了两条数据了是出现幻觉了吗这个问题就叫 幻读可重复读侧重的是添加和删除只存在幻读的问题了串行化SERIALIZABLE事务最高的隔离级别解决了脏读、不可重复读、幻读的问题但这个级别执行效率低
事务隔离级别脏读不可重复读幻读读未提交READ UNCOMMITTED√√√读已提交READ COMMITTED√√可重复读REPEATABLE READ√串行化SERIALIZABLE
Spring 中事务隔离级别有 5 种
多了一个默认事务隔离级别 DEFAULT 以连接的数据库事务隔离级别为准如果连接的是 MySQL 那么默认就是 可重复读
注意事项
当 Spring 中设置了事务隔离级别和连接的数据库MySQL事务隔离级别发送冲突的时候以Spring为准Spring 中的事务隔离级别机制的实现是依靠连接数据库支持事务隔离级别为基础
Spring 中事务隔离级别可以通过 Transactional 中的 isolation 属性进行设置 4. Spring 事务传播机制
4.1 事务传播机制是什么
Spring 事务传播机制多个事务在相互调用时事务是如何传递的
4.2 为什么需要事务传播机制
事务隔离级别是保证多个并发事务执⾏的可控性的稳定性的⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的稳定性的 4.3 事务传播机制有哪些
事务的传播机制有 7 种 Propagation.REQUIRED默认的事务传播级别它表示如果当前存在事务则加入事务如果当前没有事务则创建一个新的事务Propagation.SUPPORTS如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行Propagation.MANDATORYmandatory 强制性如果当前存在事务则加入该事务如果当前没有事务则抛出异常Propagation.REQUIRES_ENW表示创建一个新的事务如果当前存在事务则把当前事务挂起。也就是说不管外部方法是否开启事务Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务且开启的事务相互独立互不干扰Propagation.NOT_SUPPORTED以非事务方式运行如果当前存在事务则把当前事务挂起Propagation.NEVER以非事务方式运行如果当前存在事务则抛出异常Propagation.NESTED如果当前存在事务则创建一个事务作为当前事务的嵌套事务来运行如果当前没有事务则该取值等价于 PROPAGATION_REQUIRED 4.4 Spring 事务传播机制使用
4.4.1 支持当前事务REQUIRED 默认
在mycnblog数据库中先创建一个表
mysql create table loginfo(- id int primary key auto_increment,- name varchar(250),- desc text,- createtime datetime default CURRENT_TIMESTAMP);
Query OK, 0 rows affected (0.04 sec)mysql desc loginfo;
------------------------------------------------------------------------
| Field | Type | Null | Key | Default | Extra |
------------------------------------------------------------------------
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(250) | YES | | NULL | |
| desc | text | YES | | NULL | |
| createtime | datetime | YES | | CURRENT_TIMESTAMP | |
------------------------------------------------------------------------
4 rows in set (0.00 sec)以下代码实现中先开启事务先成功插入一条用户数据然后再执行日志报错而在日志报错是发生了异常观察 propagation Propagation.REQUIRED 的执行结果
RestController
public class UserController {Autowiredprivate UserService userService;Autowiredprivate LogService logService;Transactional(propagation Propagation.REQUIRED)RequestMapping(/add4)public int add4(UserInfo userInfo) {if(userInfo null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int userResult userService.add(userInfo);System.out.println(添加用户 userResult);LogInfo logInfo new LogInfo();logInfo.setName(添加用户);logInfo.setDesc(添加用户结果 userResult);int logResult logService.add(logInfo);return userResult;}
}执行结果程序报错数据库没有插⼊任何数据
执行流程 UserService 中的保存⽅法正常执⾏完成。 LogService 保存⽇志程序报错因为使⽤的是 Controller 中的事务所以整个事务回滚。 数据库中没有插⼊任何数据也就是步骤 1 中的⽤户插⼊⽅法也回滚了。 4.4.2 不支持当前事务REQUIRES_NEW
UserController 类中的代码不变将添加用户和添加日志的方法修改为 REQUIRES_NEW 不支持当前事务重新创建事务 运行程序 4.4.3 不支持当前事务NOT_SUPPORTED 4.4.4 NESTED 嵌套事务
方法调用流程Controller/add ——》 用户添加方法userservice ——》 日志添加方法logservice
当日志添加方法出现异常之后嵌套事务的执行结果是
用户添加不受影响添加用户成功日志添加失败因为发生异常回滚了事务 4.4.5 嵌套事务和加入事务的区别
先看嵌套事务 在 LogService 中进行事务的回滚操作
最终执行的效果就是User 表成功添加数据而 Log 表中没有添加数据。Log 中的事务已经回滚但是嵌套事务不会回滚嵌套之前的事务也就是说 嵌套事务可以实现部分事务回滚
加入事务 最终程序的执行结果用户表和日志表都没有添加任何数据说明整个事务都回滚了。也就是说 REQUIRED 如果回滚就是回滚所有事务不能实现部分事务的回滚 嵌套事务之所以能够实现部分事务的回滚是因为事务中有一个保存点相当于游戏存档嵌套事务进入之后相当于新建一个保存点而回滚时只回滚到当前保存点因此之前的事务是不受影响的。 而 REQUIRED 是加入到当前事务中并没有创建事务的保存点因此出现了回滚就是整个事务的回滚 总结二者区别
整个事务如果全部执行成功二者的结果是一样的如果事务执行到一半失败了那么加入事务整个事务都会回滚而嵌套事务会局部回滚不会影响上一个方法中执行的结果