赞
踩
本篇主要介绍Spring中事务相关的内容。
在操作数据库中,我们通常会遇到这样一种场景,对于一组操作,要么全部成功,要么全部失败。在数据库中,专门对这样一组操作进行了定义,那就是事务。事务具有以下四个特性:
对于事务,通常有以下三个操作:
在spring中,对于事务的操作的操作进行了重新封装,也就是我们可以在spring中使用事务相关的操作。
在 spring中使用事务,由于涉及到数据库相关的操作,需要引入mysql和mybatis等相关依赖,并配置与连接数据库相关的配置,具体配置项如下:
在spring中开使用事务有两种方式,一种是使用编程式事务,另一种则是使用声明式事务。具体如下:
使用编程式事务,通常需要使用到spring boot中内置的两个对象:
在这里有一个User类,其对应的表已在数据库中创建:
接下来我们通过编程式事务的方式来向数据库中插入 一条数据,代码如下:
通过 postman来访问一下这里的接口:
通过数据库可以发现事务已经生效了。
然后我们再来试试不提交的情况,代码如下:
再通过postMan来访问一下:
通过查询数据库可以发现事务并没有生效,admin并没有被插入
最后我们再来看一下回滚的情况
通过postMan访问 通过数据库还是可以发现admin还是没有成功插入:
通过上述过程可以发现如果不提交事务,事务就不会真的生效,那这两次没有提交的操作,真的没有去操作数据吗,其实不然。例如,我们在去通过postMan来插入一个admin2,并且这次不回滚且提交事务。
然后我们来查看一下数据库:
可以发现数据插入成功了,但id并不连续,由此可以推断出不提交事务或者回滚事务也是会去操作数据库的,但操作未提交或者回滚被抵消了。
通过前面的代码可以发现一个编程式事务存在一个问题,那就是写起来它麻烦了,因此,通常情况下我们对不使用编程式事务,而是使用另一种比较简单的声明式事务。
声明式事务的使用非常简单,只需要在方法上加一个注解即可开启事务(也可以在类上加,一味类中的全部方法都添加该注解),代码如下:
然后我们通过postMan来访问一下:
通过日志可以发现事务提交了
并且数据库里的数据库也发生了更新
由此可见声明式事务会自动帮我们创建事务并提交。那声明式事务何时会回滚呢?声明式事务通常会在发生异常时回滚,例如我们在代码里加上一个算术异常,具体如下:
访问一下看下效果:
通过日志可以发现日志并没有被提交就被关闭了
然后我们再看数据库中的数据,可以发现,并没有我们的admin5
那是不是所有异常都会触发回滚呢?其实不然,声明式事务默认只有在发生运行时异常时和错误时会发生回滚 ,例如我们这里抛出一个IO异常(在方法后面进行声明):
通过postMan来访问:
通过日志可以发现,事务仍然提交了:
我们也可以将发生触发回滚的异常通过rollbackfor来进行指定,具体如下:
这里我们设置成了所有异常都触发回滚,此时我们再以上面抛出IO异常的代码来新增数据,可以发现这次日志没有提交,而是进行了回滚
有时候,对于发生的一些异常我们并不想让其回滚,也不想因为一个异常而修改整个发生回滚的异常的类型,此时我们可以考虑使用try-catch来将这个异常catch住,这样@Transcation就感知不到异常的发生也就无法回滚,如果我们只想在发生该异常时进行一些额外处理,我们可以在处理后进行手动回滚,具体如下:
通过这行代码也可以大致推断出@Transcation其实是为加了注解的方法创建了一个切面(Aspect),在切面里封装了前面编程式事务的内容从而实现事务。
在数据库中,对于事务的隔离性按照从低到高的顺序进行了分级,分别为:
在这四个隔离级别中前三个隔离级别会发生一些问题,具体如下:
而使用串行化由于并没有并发执行的事务,所以不存在上述问题。
在spring中也设置了事务的隔离级别,具体如下:
1. Isolation.DEFAULT :以连接的数据库的事务隔离级别为主.
2. Isolation.READ_UNCOMMITTED :读未提交,对应SQL标准中READ UNCOMMITTED
3. Isolation.READ_COMMITTED :读已提交,对应SQL标准中READ COMMITTED
4. Isolation.REPEATABLE_READ :可重复读,对应SQL标准中REPEATABLE READ
5. Isolation.SERIALIZABLE :串⾏化,对应SQL标准中SERIALIZABLE
对于隔离级别,我们同样可以在@Transcation注解里进行设置,例如我们想将隔离级别改为可重复读,代码如下:
修改完成之后,通过该方法创建的事务的隔离级别就是可重复读了。如果我们不去自己设置隔离级别,则使用默认的隔离级别,也就是使用数据库中使用的隔离级别。
在使用事务时通常有可能会出现在开启的一个事务中又开启了一个事务的情况,例如,我们将前面的代码里在封装一层,具体如下:
封装的service层
像这种事务在开启事务的情况称为事务的传播。
对于事务的传播spring 设置了不同的传播机制,具体如下:
在spring中我们可以通过设置propagation参数来设置当前事务使用哪种传播机制,具体如下:
下面我们来演示一下在调用方法和被调用方法使用REQUIRED 、REQUIRED_NEW、NESTED这几种传播机制会发生的效果。然后我们来创建一个算术异常来控制是否回滚 。
首先我们来看一下不回滚的情况:
调用方法
被调用方法
两个方法都开启了事务
被调用方法使用REQUIRED机制:
访问结果:
此时只有一个事务提交
使用REQUIRED_NEW:
访问结果:
可以发现在原本事务的 基础上开启了一个新的事务.并且新旧事务都提交了。
使用 NETSTED
访问结果:
可以发现这种机制只有一个提交,因为创建的新事务嵌套在了旧事务里,所以一起提交了
我们在来看一下发生回滚的情况
代码如下:
使用REQUIRED:
访问结果:
可以发现事务回滚了且没有事务提交,因为在这种机制下新旧方法都使用的一个事务,一旦里面有一个方法里出现了异常,两个方法都得回滚。
使用REQUIRED_NEW的情况
访问结果
这种机制下由于两个方法处于不同的事务中,因此某一个事务中出现异常回滚,并不会影响另一个事务的运行,因此新事务回滚了,旧事务仍然提交了
使用NETSTED的情况
访问结果
这种机制下新事务会嵌套在旧事务里,并保存开启新事务前的一个状态,当新事务发生回滚时,回滚到保存的这个状态点,而不是回到旧事务开启时的状态因此旧的事务不会被回滚而是继续提交了。
其它机制的描述比较直观,这里就不多赘述了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。