这里的 事务 和之前 MySQL的事务 一样,都是表示将⼀组操作封装成⼀个执⾏单元(封装到⼀起),要么全部成功,要么全部失败。
Spring 中事务的实现
1. 编程式事务(手动档)。
package com.example.transactiondemo.controller;
import com.example.transactiondemo.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// 编程式事务
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/del")
public int del(Integer id) {
if (id == null || id <= 0) return 0;
// 1.开启事务
TransactionStatus transactionStatus = null;
int result = 0;
try {
transactionStatus = transactionManager.getTransaction(transactionDefinition);
// 业务操作,删除用户
result = userService.del(id);
System.out.println("删除操作影响了:" + result + " 行");
// 2. 提交事务/回滚事务
transactionManager.commit(transactionStatus); // 提交事务
} catch (Exception e) {
if (transactionStatus != null) {
transactionManager.rollback(transactionStatus); // 出现异常,回滚事务
}
}
return result;
}
}
运行结果:
2. 声明式事务(自动挡)。
package com.example.transactiondemo.controller;
import com.example.transactiondemo.Service.UserService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user2")
public class UserController2 {
@Autowired
private UserService userService;
@RequestMapping("del")
@Transactional // 在方法开始之前开启事务,方法正常执行结束之后提交事务,如果执行途中发生异常,则回滚事务
public int del(@Param("id") Integer id) {
if (id == null || id <= 0) {
return 0;
}
int result = userService.del(id);
System.out.println("删除操作影响了:" + result + " 行");
return result;
}
}
没有发生异常的情况下的运行结果:
1. 发生了异常情况下的运行结果(发生了回滚):
2. 但是这种代码的书写会导致程序直接异常而不能进行下一步了,为了更好解决这一问题就需要使用 try-catch 把错误代码包裹住:
第 1 种是将异常抛出去,然后 spring 感知到了就自动帮我们实现回滚了,第 2 种是自己手动处理,因为 spring 会认为我们既然都用了 try-catch 了,那么就是知道会有异常发生了,从而不会帮我们自动实现回滚。
关于 @Transactional
单元测试里:只要执行完都会进行回滚
普通类里:只有出现异常了才会进行回滚
参数的设置:
工作原理
通过切面,通过动态代理实现:
@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使用 CGLIB 动态代理。
@Transactional 在开始执行业务之前,通过代理先开启事务,在执行成功之后再提交事务。如果中途遇到的异常,则回滚事务。
设置事务隔离级别
Isolation.DEFAULT 是 spring 自己的,表示以连接的数据库的事务隔离级别为主。(Oracle 的默认隔离级别是 读已提交)
MySQL 事务隔离级别有 4 种:(默认的是 可重复读)
● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。
关于 spring boot 中事务会失效的情况
1.非public修饰的方法
2.timeout超时:当在 @Transactional 上,设置了一个较小的超时时间时,如果方法本身的执行时间超过了设置的 timeout 超时时间,那么就会导致本来应该正常插入数据的方法执行失败
3. 代码中有try/catch,也没进行手动回滚操作
4.当前数据库本身不支持事务