文章目录
Spring事务概述
声明式事务:声明式事务也有两种实现方式,基于 xml 配置文件的方式和注解方式(在类上添加@Transaction 注解)
编码方式:提供编码的形式管理和维护事务
编程事务:手动begin、commit/rollback操作 代码重复
- 事务的4个特性:原子性,一致性,隔离性,持久性
- 两种事务管理方式:声明式和编程式。我们一般采用声明式事务管理。
- 声明式事务:基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。
Spring事务的五种事务隔离级别
- ISOLATION_DEFAULT 这是⼀个PlatfromTransactionManager默认的隔离级别,使⽤数据库默认的事务隔离级别.
另外四个与JDBC的隔离级别相对应: - ISOLATIONREADUNCOMMITTED 这是事务最低的隔离级别,它充许别外⼀个事务可以看到这个事务未提交的数据。这种隔离级别会产⽣脏读,不可重复读和幻读。
- ISOLATIONREADCOMMITTED 保证⼀个事务修改的数据提交后才能被另外⼀个事务读取。另外⼀个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。
- ISOLATIONREPEATABLEREAD 这种事务隔离级别可以防⽌脏读,不可重复读。但是可能出现幻读。它除了保证⼀个事务不能读取另⼀个事务未提交的数据外,还保证了避免不可重复读。
- ISOLATION_SERIALIZABLE 这是花费最⾼代价但是最可靠的事务隔离级别。事务被处理为顺序执⾏。除了防⽌脏读,不可重复读外,还避免了幻读。
spring 7种事务的传播行为
1.REQUIRED 如果当前没有事务,就新建⼀个事务,如果已经存在⼀个事务中,加⼊到这个事务中。这是最常⻅的选择 。默认的 Spring 事物传播级别
2.REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起,不管是否存在事务,都会新开一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交
3.NESTED:如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务,则新建一个事务,类似于REQUIRE_NEW
4.SUPPORTS 表示支持当前事务,如果当前不存在事务,以非事务的方式执行
5.MANDATORY 使⽤当前的事务,如果当前没有事务,就抛出异常
6. NEVER 以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常
7.NOT_SUPPORTED 以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。
Spring 事务底层原理
因为spring事务底层是用了aop,用了jdk的动态代理或者cglb的动态代理,会帮我们生成代理类,在代理类中实现事务功能。
a、划分处理单元——IOC
由于 spring 解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用 spring
中的 IOC 划分了事务处理单元。并且将对事务的各种配置放到了 ioc 容器中(设置事务管
理器,设置事务的传播特性及隔离机制)。
b、AOP 拦截需要进行事务处理的类
Spring 事务处理模块是通过 AOP 功能来实现声明式事务处理的,具体操作(比如事务实
行的配置和读取,事务对象的抽象),用 TransactionProxyFactoryBean 接口来使用 AOP功能,生成 proxy 代理对象,通过 TransactionInterceptor 完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取 ioc 容器事务配置属性,转化为 spring 事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute 表示的数据对象。
c、对事物处理实现(事务的生成、提交、回滚、挂起)
spring 委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource 数据源支持、hibernate 数据源事务处理支持、JDO 数据源事务处理支持,JPA、JTA 数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction 一系列事务处理的支持。 为常用数据源支持提供了一系列的 TransactionManager。
d、结合PlatformTransactionManager 实现了 TransactionInterception 接口,让其与TransactionProxyFactoryBean 结合起来,形成一个 Spring 声明式事务处理的设计体系。
spring 声明式事务哪些场景会失效
1、@Transaction事务注解添加在不是public修饰的方法上
2、类没有被Spring托管,导致Spring无法实现代理
3、抛出异常,被catch处理了,导致@Transaction无法回滚而失效
4、调用同一个类中的方法,导致@Transaction失效
5、propagation事务传播特性配置错误
6、rollbackFor参数设置错误
7、数据库本身不支持事务,数据库配置不支持事务的数据库引擎,
8.多线程调用,两个方法不在同一个线程中,获取到数据库连不一样。
9.方法没有被public修饰
10、同一类中方法调用
11、不正确的异常捕获
12、没有配置事务管理器
访问权限问题
java的访问权限有4种:private、default、protected、public,它们的权限从左到右,以此变大。如果在开发中,将事务方法定义了错误的访问权限,则事务功能会失效。
如上:add方法的权限被定义成了private,这样会导致事务失效,spring要求被代理方法必须是public的。
在Spring源码中,如果目标方法不是public,则TransactionAttribute返回null,不支持事务
jdk 或者 cglib 实现类 是private无法继承实现
方法用final修饰
放方法被final修饰时,也会导致事务失效。
因为spring事务底层是用了aop,用了jdk的动态代理或者cglb的动态代理,会帮我们生成代理类,在代理类中实现事务功能。
如果某个方法被final修饰了,那么在代理类中,就无法重新该方法,而添加事务功能。
注意:如果某个方法被static修饰,同样也无法通过动态代理,变成事务方法。
直接调用内部方法
@Service
public class EmpService {
@Transactional
public void add(UserModel userModel){
saveData(userModel);
updateSataus(userModel);
}
@Transactional
public void updateSataus(UserModel userModel){
doSomething();
}
}
由上可知:在事务方法add可知,它直接调用了updateStatus方法,由2可知:方法拥有事务的能力是因为spring aop中生成了代理对象,但是直接调用updateStatus方法不会直接生成事务。但是可以直接将该类直接注入进来,比如:
@Service
public class EmpService {
private EmpService empService;
@Transactional
public void add(UserModel userModel){
saveData(userModel);
empService.updateSataus(userModel);
}
@Transactional(rollbackFor = Exception.class)
public void updateSataus(UserModel userModel){
doSomething();
}
}
这样事务就生效了,也不会穿生循环依赖的问题。
未被Spring管理
多线程调用
由以下代码可知:在add()事务方法里面,调用了updateStatus事务方法,但是updateStatus事务方法是在另外一个线程中调用的。这样就导致了两个方法不在同一个线程中,获取到了数据库连接不一样,从而是两个不同的事务,如果updateStatus方法中抛出了异常,add方法是不会回滚的
@Service
public class EmpService {
@Autowired
private OrderService orderService;
@Transactional
public void add(UserModel userModel){
new Thread(()->{
orderService.updateSataus();
}).start();
}
}
@Service
public class OrderService{
@Transactional
public void updateSataus(){
System.out.println("======================");
}
}
spring的事务是通过数据库的连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。同一个事务,指同一个数据库连接,只有拥有同一个事务连接才能保证同时提交和回滚。如果是不同的线程,拿到的数据库连接肯定是不同的。
表不支持事务
如果表的引擎是myisam,那么它是不支持事务的,要想支持事务,改成innodb引擎
事务没有开启
如果是springBoot项目,那么是事务默认是开启的,但如果是spring项目,需要xml配置
事务的传播特性
如果事务的传播特性设置错了,事务也会失效。如下:propagation = Propagation.NEVER这种类型的传播特性不支持事务,如果有事务会抛出异常。
目前只有这三种传播特性才会创建新事物:REQUIRED、REQUIRES_NEW、NESTED
@Service
public class EmpService {
@Transactional(propagation = Propagation.NEVER)
public void add(UserModel userModel){
saveData(userModel);
updateSataus(userModel);
}
}
自己吞了异常
事务不会回滚,最常见的问题是:开发者在代码中手动try…catch了异常。如下:
@Service
public class EmpService {
@Transactional
public void add(UserModel userModel){
try {
saveData(userModel);
updateSataus(userModel);
} catch (Exception e) {
e.printStackTrace();
}
}
}
这种情况下,因为开发者自己捕获了异常、又没有手动抛出
手动抛了别的异常
如果抛的异常不正确,事务也不会回滚
@Service
public class EmpService {
@Transactional
public void add(UserModel userModel) throws Exception {
try {
saveData(userModel);
updateSataus(userModel);
} catch (Exception e) {
throw new Exception(e);
}
}
}
因为Spring事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),对于普通的非运行时异常,它不会回滚。
自定义回滚异常
如果在使用@Transactional注解声明事务时,有时想自定义回滚异常,spring也是支持的。可以通过设置rollbackFor参数,来完成这个功能。如下:
@Service
public class EmpService {
@Transactional(rollbackFor = BusinessException.class)
public void add(UserModel userModel) {
saveData(userModel);
updateSataus(userModel);
}
}
但是如果在程序执行过程中,出现了sql异常,但是sql异常并不属于我们定义的BusinessException异常,所以事务也不会回滚
延伸:
1 嵌套事务导致多回滚了怎么办?
@Service
public class EmpService {
@Autowired
private OrderService orderService;
@Transactional
public void add(UserModel userModel) {
saveData(userModel);
orderService.updateSataus();
}
}
@Service
public class OrderService(){
@Transactional(propagation = Propagation.NESTED)
public void updateSataus(){
System.out.println("======================");
}
}
如果说对于这样的代码。如果saveData执行成功了,updataStatus执行失败了,那么整个add方法就会回滚。那么之前saveData执行成功的这个方法也会回滚。
那如何只让报错的方法进行回滚呢?只需要将报错的方法抓取异常就OK了。如下:
@Service
public class EmpService {
@Autowired
private OrderService orderService;
@Transactional
public void add(UserModel userModel) {
saveData(userModel);
try {
orderService.updateSataus();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Service
public class OrderService(){
@Transactional(propagation = Propagation.NESTED)
public void updateSataus(){
System.out.println("======================");
}
}
使用Spring 事务有哪些注意事项
如果使用事务注解,方法中有异常往上抛 不要try,否则 外层aop没有
拦截到异常 导致事务注解会失效,如果非要try的情况下 可以采用手动事务处理。
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 手动回滚
@GetMapping("/updateUser2")
@Transactional
public String updateUser2(String name) {
try {
int result = userMapper.updateUser(name);
if ("mayikt".equals(name)) {
// 报错
int j = 1 / 0;
}
return result > 0 ? "ok" : "fail";
} catch (Exception e) {
log.error("e:{}", e);
TransactionAspectSupport.
currentTransactionStatus()
.setRollbackOnly(); // 手动回滚
return "fail";
}
}
Spring的事务是如何回滚的
spring的事务管理是如何实现的
建立连接开启事务 进行sql操作 执行成功commit 失败rollback
总: spring的事务是由aop来实现的,首先要生成具体的代理对象,然后按照sop的整套流程来执行具体的操作流程,正常情况下要通过通知来完成核心功能,但是事务不是通过通知来实现的,而是通过TransactionInterceptor来实现的,然后调用incoke来实现具体的逻辑分:
1.先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开启新事物
2.当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务
3.执行具体的sql逻辑操作
4.在操作过程中,如果执行失败了,那么会通过competeTransactionThrowing看来完成事务的回滚操作,回滚的具体逻辑是通过doRollBack方法来实现的,实现的时候也是要先获取连接对象,通过连接对象来回滚
5.如果执行过程中,没有任何意外情况的发生,那么通过commitTramsactionAfterReturning来完成事务的提交操作,提交的具体逻辑是通过doCommit方法来实现的,实现的时候也是要获取连接,通过连接对象来提交
6.当事务执行完毕之后需要清楚相关的事务信息clranupTransactionInfo
8765

被折叠的 条评论
为什么被折叠?



