专栏前言
大家好,我是执梗。本专栏将从Spring
入门开始讲起,详细讲解各类配置的使用以及原因,到使用SpringBoot
进行开发实战,旨在记录学习生活的同时也希望能帮到大家,如果对您能有所帮助,还望能点赞关注该专栏,对于专栏内容有错还望您可以及时指点,非常感谢大家 🌹。
目录
1.为什么需要事务?
事务这东西大家在学习Mysql
时肯定就已经接触过了,我们先回顾其定义:将一组操作封装成一个原子操作,要么全部成功,要么全部失败。
可以说事务是数据库数据安全的保障,在以前Mysql
中我们事务有三个重要的操作:
-
start transaction
开启事务
-
commit
提交事务
-
rollback
回滚事务
2.Spring 中的事务
Spring
中支持两种事务编程模型,一种叫做编程式事务,一种叫声明式事务。下面我们会主动来讲他们两个的实现以及区别,其实大家可以将两者类比为手动挡的车和自动挡的车的区别。
这里有一个非常重要的一点,即Spring
对事务的支持是基于你的数据库是否支持事务。 如果你的数据库引擎是InnoDB
,那么你的数据库是支持事务的,如果是MyISAM
的话那么就不支持事务。
3.编程式事务
编程式事务好比与我们的手动挡的车,一切操作都需要程序员通过代码去手动实现。它的操作步骤和Mysql
是一样的,分别是开启事务、提交事务、回滚事务。
SpringBoot
中实现编程式事务又要两种方法:
- 1.使用
TransactionTemplate
对象实现编程式事务 - 2.使用
TransactionManager
对象实现编程式事务
学习之前大家须知平时我们并不会使用编程式事务,因为它操作比较麻烦,大家通过下面的代码实现也是可以看出来的,但其优点是比较灵活。
3.1使用 TransactionTemplate 实现事务
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private TransactionTemplate transactionTemplate;
@Resource
private UserService userService;
@RequestMapping("/add")
public int add(User user) {
//非空校验
if (user == null || !StringUtils.hasLength(user.getUsername()) ||
StringUtils.hasLength(user.getPassword())) return 0;
// 开启并执行事务
return transactionTemplate.execute(status -> {
int result = 0;
try {
//业务代码
result = userService.add(user);
} catch (Exception e) {
//若产生异常则回滚
status.setRollbackOnly();
}
return result;
});
}
}
3.2使用 TransactionManager 实现事务
@RestController
public class UserController {
@Resource
private UserService userService;
// JDBC 事务管理器
@Resource
private DataSourceTransactionManager dataSourceTransactionManager;
// 定义事务属性
@Resource
private TransactionDefinition transactionDefinition;
@RequestMapping("/add")
public int add(User user) {
//非空校验
if (user == null || !StringUtils.hasLength(user.getUsername()) ||
StringUtils.hasLength(user.getPassword())) return 0;
// 开启事务
TransactionStatus transactionStatus =
dataSourceTransactionManager
.getTransaction(transactionDefinition);
try{
// 插⼊数据库
int result = userService.add(user);
// 提交事务
dataSourceTransactionManager.commit(transactionStatus);
}catch{
// 回滚事务
dataSourceTransactionManager.rollback(transactionStatus);
}
return result;
}
}
这里注意到一个对象为transactionDefinition
,它是管理我们的事务属性的。
那么什么是事务属性呢?
事务属性包含5
个方面:
- 1.隔离级别
- 2.传播行为
- 3.回滚规则
- 4.是否超时
- 5.事务超时
transactionDefinition
中就定义了以上这些基本的事务属性的常量,大家可以自行深入了解,当然我们也可以使用对应的方法去设置这些值。
4.声明式事务
声明式事务的实现就非常简单了,只需要在方法或者类上加上注解 @Transactional
即可。这样在方法执行前,它将会自动开始事务,在结束后自动提交事务,若中途发生异常,会自动回滚事务。下面是代码的实现:
@RequestMapping("/add")
@Transactional
public int add(User user) {
//非空校验
if (user == null || !StringUtils.hasLength(user.getUsername()) ||
StringUtils.hasLength(user.getPassword())) return 0;
int result = userService.add(user);
return result;
}
@Transactional
注解中可以添加许多参数,每个参数的含义如下,这里就包含了设置事务属性的方法
当然我们前面提了@Transactional
也是可以加在类上的,当加到类上时,其作用是为该类中所有public
方法开启事务。
还有一点需要注意的是,在声明式事务中,如果产生异常且该异常被捕获了,此时是不会进行回滚的。解决方法有两种:
- 1.将捕获的异常重新抛出去
@RequestMapping("/add")
@Transactional(isolation = Isolation.SERIALIZABLE)
public int add(User user) {
// 插⼊数据库
int result = userService.add(user);
try {
// 执⾏了异常代码(0不能做除数)
int i = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
// 将异常重新抛出去
throw e;
}
return result;
}
- 2.手动进行回滚,在方法中使用
TransactionAspectSupport.currentTransactionStatus()
获取事务后,调用setRollbackOnly
回滚方法手动回滚。
@RequestMapping("/add")
@Transactional(isolation = Isolation.SERIALIZABLE)
public int save(User user) {
// 插⼊数据库
int result = userService.add(user);
try {
// 执⾏了异常代码(0不能做除数)
int i = 10 / 0;
} catch (Exception e) {
System.out.println(e.getMessage());
// ⼿动回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
5. @Transactional 工作原理
@Transactional
是基于AOP
进行实现的,而AOP
又是通过动态代理进行实现的。同理可知如果目标对象实现了接口则使用Java自带的动态代理,否则使用CGLIB动态代理。