@TOC
回顾 事务
- 事务的本质就是
某个操作要么都成功, 要么都失败
- 事务在项目开发中十分重要,
涉及到数据一致性和完整性问题
, 不能马虎 - 事务的
ACID
原则
原子性
一致性
隔离性
, 多个业务可能操作同一个资源, 防止数据损坏持久性
, 事务一旦发生, 无论系统发生什么问题, 结果都不会再被影响, 被持久的写到存储器中
Spring 中事务的实现
Spring 中的事务操作分为两类:
⼿动操作事务
声明式⾃动提交事务
在开始讲解它们之前,咱们先来回顾事务在 MySQL 中是如何使⽤的?
MySQL 中的事务使⽤(回顾)
事务在 MySQL 有 3 个重要的操作:
- 开启事务
- 提交事务
- 回滚事务
它们对应的操作命令如下:
-- 开启事务
start transaction;
-- 业务执⾏
-- 提交事务
commit;
-- 回滚事务
rollback;
Spring 编程式事务(手动操作)
Spring ⼿动操作事务和上⾯ MySQL 操作事务类似,它也是有 3 个重要操作步骤:
- 开启事务(获取事务)getTransaction
- 提交事务 commit
- 回滚事务 rollback
SpringBoot 内置了两个对象:
DataSourceTransactionManager
⽤来 获取事务(开启事务)、提交或 回滚事务
TransactionDefinition
是 事务的属性
获取事务的时候需要将 TransactionDefinition
传递进去从⽽获得⼀个事务 TransactionStatus
实现代码如下:
@RestController
public class UserController {
@Resource
private UserService userService;
// 注入 JDBC 事务管理器
@Resource
private DataSourceTransactionManager dataSourceTransactionManager;
// 注入 定义事务属性
@Resource
private TransactionDefinition transactionDefinition;
@RequestMapping("/sava")
public Object save(User user) {
// 开启事务
TransactionStatus transactionStatus =
dataSourceTransactionManager
.getTransaction(transactionDefinition);
// 插⼊数据库
int result = userService.save(user);
// 提交事务
dataSourceTransactionManager.commit(transactionStatus);
// 回滚事务
dataSourceTransactionManager.rollback(transactionStatus);
return result;
}
}
从上述代码可以看出,以上代码虽然可以实现事务,但操作也很繁琐,有没有更简单的实现⽅法
呢?请 看下⾯声明式事务
。
Spring 声明式事务(自动事务)
声明式事务
的实现很简单,只需要在需要的⽅法上添加 @Transactional 注解
就可以实现了
⽆需⼿动 开启事务和提交事务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务
具体实现代码如下:
@RequestMapping("/save")
@Transactional //开启事务
public Object save(User user) {
int result = userService.save(user);
return result;
}
接下⾥使⽤以下代码,分别设置 @Transactional 注解和不设置 @Transactional
观察它们的执⾏区别:
- 开启事务时,如果代码运行时发生异常会会进行回滚
- 没有开启事务时,如果代码运行时发生异常不会进行回滚,代码依然会进行操作
@Transactional 作⽤范围
@Transactional 可以⽤来修饰⽅法或类:
修饰⽅法
时:需要注意只能应⽤到 public ⽅法上
,否则不⽣效。推荐此种⽤法。修饰类
时:表明该注解对该类中所有的 public ⽅法都⽣效
。
@Transactional 参数说明
注意事项
@Transactional 在异常被捕获的情况下,不会进⾏事务⾃动回滚
。
验证以下代码是否会发⽣事务回滚:
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/save")
@Transactional
public Object save(User user) {
// 插⼊数据库
int result = userService.save(user);
try {
// 执⾏了异常代码(0不能做除数)
int i = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return result;
}
}
事务不会自动回滚解决⽅案
解决⽅案1:将捕获异常重新抛出
对于捕获的异常,事务是会⾃动回滚的
因此解决⽅案1就是可以将异常重新抛出
,具体实 现如下:
@RequestMapping("/save")
@Transactional(isolation = Isolation.SERIALIZABLE)
public Object save(User user) {
// 插⼊数据库
int result = userService.save(user);
try {
// 执⾏了异常代码(0不能做除数)
int i = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
throw e; // 将异常重新抛出去
}
return result;
}
解决⽅案2:手动回滚事务
在⽅法中使⽤ TransactionAspectSupport.currentTransactionStatus( )
可 以得到当前的事务
设置回滚⽅法 setRollbackOnly
就可以实现回滚了
具体实现代码如下:
@RequestMapping("/save")
@Transactional(isolation = Isolation.SERIALIZABLE)
public Object save(User user) {
// 插⼊数据库
int result = userService.save(user);
try {
// 执⾏了异常代码(0不能做除数)
int i = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
// ⼿动回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
@Transactional ⼯作原理
@Transactional 是基于 AOP 实现的
,AOP ⼜是使⽤动态代理实现
的。
如果⽬标对象实现了接⼝
,默认 情况下会采⽤ JDK
的动态代理
如果⽬标对象没有实现了接⼝
,会使⽤ CGLIB
动态代理。
@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇 到的异常,则回滚事务。
@Transactional 实现思路预览:
@Transactional 具体执⾏细节如下图所示: