您现在的位置是:首页 >学无止境 >Spring事务网站首页学无止境
Spring事务
作者:~小明学编程
文章专栏:Spring框架
格言:热爱编程的,终将被编程所厚爱。
目录
回忆事务
事务是把一组操作封装成一个执行单元(封装到一起),要么全部成功,要么全部失败。
实现事务的原理:是通过 日志 来实现的。会记录一个日志,只有事务的开始和事务的执行,但是没有事务的结束。等下一次恢复的时候,会进行日志的自检,如果发现日志只执行了一半,没执行完,就会执行补偿机制。
Spring实现事务
spring中实现事务主要有两种方式:
- 编程式事务(手动写代码操作事务)
- 声明式事务(里有注解自动开启和提交事务)
Spring中提供了相应的类来帮助我们去实现事务的开启提交和回滚。
- DataSourceTransactionManager:⽤来获取事务(开启事务)、提交或回滚事务的。
- TransactionDefinition:是事务的属性,在获取事务的时候需要将TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus。
编程式事务
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/add")
public int add(UserInfo userInfo) {
//非空校验
if (userInfo==null||!StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {
return 0;
}
//1.开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
// 设置创建时间和修改时间的默认值
userInfo.setCreatetime(LocalDateTime.now().toString());
//设置修改时间
userInfo.setUpdatetime(LocalDateTime.now().toString());
int result = userService.add(userInfo);
System.out.println("添加:"+result);
//2.回滚事务
// transactionManager.rollback(transactionStatus);
//提交事务
transactionManager.commit(transactionStatus);
return result;
}
}
可以看到我们操作事务起来是比较的繁琐的这自然是不如我们的注解来的方便。
声明式事务@Transactional
我们可以采用 @Transactional 注解来完成对事务的操作,其中使用了该注解之后就自动的开启我们的事务了,当被注解修饰的地方的代码顺利的执行完了之后那么就提交该事务,但是如果出现中途代码异常的话就会自动的回滚我们的事务。
@Transactional//声明事务,自动提交
@RequestMapping("/insert")
public int insert(UserInfo userInfo) {
//非空校验
if (userInfo==null||!StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {
return 0;
}
int result = userService.add(userInfo);
int num = 10/0;
return result;
}
Transactional的作用范围
@Transactional 注解也可以用来修饰类或方法:
- 修饰方法时,需要注意的是,只能应用到 public 方法上,否则不生效。
- 修饰类时,表示这个注解对类中所有的 public 方法都生效。都会自动开启和提交 / 回滚事务。
Transactional中的异常
我们会遇到一种情况,那就我是在我们使用事务声明的时候如果代码中发生了异常此时如果我们的使用了try,catch来处理异常的话那么我们的事务就不会发生回滚,我们可以把它看作是我们的bug当然换句话说这个也是我们代码的bug。
想要解决上述的问题的话我们有两种方式:
- 我们手动的在try,catch中去抛出一个异常。
- 通过代码手动的去回滚这个事务。
@Transactional//声明事务,自动提交
@RequestMapping("/insert")
public int insert(UserInfo userInfo) {
//非空校验
if (userInfo==null||!StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {
return 0;
}
int result = userService.add(userInfo);
try {
int num = 10/0;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return result;
}
对于第一种的写法我们显然是很不认同的,因为我们捕获了一个异常然后再将这个异常给抛出去,这种写法属实有些拉跨,我们一般会采用第二种写法,手动的回滚我们的当前事务,这样子就很优雅了。
@Transactional//声明事务,自动提交
@RequestMapping("/insert")
public int insert(UserInfo userInfo) {
//非空校验
if (userInfo==null||!StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {
return 0;
}
int result = userService.add(userInfo);
try {
int num = 10/0;
} catch (Exception e) {
// e.printStackTrace();
// throw e;
//手动的去回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
@Transactional 工作原理
- @Transactional 是基于 AOP 实现的,AOP 优势使用动态代理实现的。
- 如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
- @Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。
事务的隔离级别
关于mysql中事务的隔离级别我前面已经总结过了,想要了解详情的话可以查看下面这个链接:
Spring事务中的隔离级别
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置。
在Spring的事务中我们有五种隔离级别,他们分别是:
- Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
- Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
- Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
- Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
- Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。
Spring事务的传播机制
事务的传播机制
事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)。
Spring 事务传播机制定义了多个包含了事务的方法,相互调用时,事务是如何在这些方面进行传递的呢?
- Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。就像结婚买房,房子(事务)是必须项。女方就是:男方买房子,就住进去,如果男方没房子,也没事,一起努力买房,但是房子必须有。
- Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。继续使用上面的例子,也就是说,男方有没有房子都无所谓,就想和他结婚。有房子,就住。没有,就租房。
- Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。就相当于,如果男方没钱,就不结婚,甚至直接分手。有的话,女方就直接住进去。
- Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。就相当于男方已经买了房,但是女生表示不是新房,我不干了。
- Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。就相当于,男方有房子,女方也不住,就是要租房,
- Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。就相当于,男方有房子,就不结婚了,直接分手。如果没有房子,那我就和你结婚。
- Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏。如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED(创建一个新事物)。就相当于是:男方有房,女方家里又给了一套房,如果男生没有房,女方表示也可以直接送一套。