1. 为什么需要事务?
前面的博客 对MySQL事务作讲解,事务就是将⼀组操作封装成⼀个执⾏单元(封装到⼀起),要么全部成功,要么全部失败。
比如,现在要实现转账操作:
第一步:张三账户 -500
第二步:李四账户+500
如果没有事务,第一步执行成功,然而第二步执行失败,那么张三的账户的500元就凭空消失了。而事务的引入就可以解决这个问题,让这一组操作要么一起成功,要么一起失败。
2.Spring中事务的实现
Spring中的事务操作分为两类:
- 编程式事务(⼿动写代码操作事务)。
- 声明式事务(利⽤注解⾃动开启和提交事务)。
之前MySQL博客中,使用事务可以分为3个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:
-- 开启业务 (⼀组操作前开启事务)
start transaction;
-- 相关业务执行-- 提交事务 (这组操作全部成功, 提交事务)
commit;-- 回滚事务 (这组操作中间任何⼀个操作出现异常, 回滚事务)
rollback;
2.1 Spring编程式事务(手动)
- 开启事务(获取)
- 提交事务
- 回滚事务
使用编程式事务代码如下:
代码结构:
controller:
@RestController
@RequestMapping("user")
public class UserController {@Autowiredprivate UserService userService;// JDBC 事务管理器@Resourceprivate DataSourceTransactionManager dataSourceTransactionManager;// 定义事务属性@Resourceprivate TransactionDefinition transactionDefinition;@PostMapping("/add")public String addUser(@RequestBody Userinfo user) {// 开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);userService.addUser(user);//提交事务dataSourceTransactionManager.commit(transactionStatus);//回滚事务//dataSourceTransactionManager.rollback(transactionStatus);return "User added successfully!";}
}
当执行提交事务,观察结果:
数据库:
发现我们程序执行成功并且事务提交完成,持久化到数据库中。
那么如果我们使用的是回滚代码,来添加用户tom,再来观察执行结果:
然而,数据却没有同步到数据库中,因为事务被我们回滚了:
以上代码虽然可以实现事务, 但操作也很繁琐, 有没有更简单的实现⽅法呢? --- 声明式事务
2.2 声明式事务@Transactional
注意:我们⼀般会在业务逻辑层当中来控制事务, 因为在业务逻辑层当中, ⼀个业务功能可能会包含多个数据访问的操作. 在业务逻辑层来控制事务, 我们就可以将多个数据访问操作控制在⼀个事务范围内. 下面代码在Controller中书写, 只是为了⽅便演示学习.
@Transactional
@RestController
@RequestMapping("user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/add")public String addUser(@RequestBody Userinfo user) {userService.addUser(user);return "User added successfully!";}
}
现在代码中是没有异常的,也就是实行玩添加业务后就提交了。添加 tom,观察执行结果:
入预期所料,事务被提交,数据持久化到数据库。
现在我们手动来写一个异常,然后添加alice,通过触发异常来实现事务回滚:
观察结果,数据被回滚:
@Transactional作用
@Transactional 可以⽤来修饰⽅法或类:
- 修饰⽅法时: 只有修饰public ⽅法时才⽣效(修饰其他⽅法时不会报错, 也不⽣效)[推荐]
- 修饰类时: 对 @Transactional 修饰的类中所有的 public ⽅法都⽣效.
- 如果在⽅法执⾏过程中, 出现异常, 且异常未被捕获, 就进⾏事务回滚操作.
- 如果异常被程序捕获, ⽅法就被认为是成功执⾏, 依然会提交事务.
运⾏程序, 发现虽然程序出错了, 但是由于异常被捕获了, 所以事务依然得到了提交.
如果需要事务进⾏回滚, 有以下两种方式:
1. 重新抛出异常:
2. 手动回滚事故
3. @Transactional详解
了解 @Transactional 注解当中的三个常⻅属性:
3.1 rollbackFor
观察结果:
发现虽然程序抛出了异常, 但是事务依然进⾏了提交:
再次执行程序,发现虽然程序抛出了IOException异常,但是事务也回滚了,因为我们修改了rollbackFor 的属性。
结论:
- 在Spring的事务管理中,默认只在遇到运⾏时异常RuntimeException和Error时才会回滚.
- 如果需要回滚指定类型的异常, 可以通过rollbackFor属性来指定.
写累了.事务的隔离级别和传播机制单独开一节博客来回估吧。