SpringBoot事务

了解事务之前得了解几个概念:

1.脏读:

脏读简单来说就是提取未提交的数据

A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。就好像原本的数据比较干净、纯粹,此时由于B事务更改了它,这个数据变得不再纯粹。这个时候A事务立即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了此次的脏数据,称为脏读。

这种情况常发生于转账与取款操作中


脏读

2.不可重复读

简单来说就是前后多次读取,数据内容不一致

事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。


不可重复读

3.幻读

事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

幻读

数据库事务


事务的基本要素(ACID)

1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

 2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。


事务隔离


数据库有四种隔离级别:

读未提交(Read uncommitted)

读已提交(Read committed)

可重复读(Repeatable read)

可串行化(Serializable)

读未提交

在这种隔离级别下,所有事务能够读取其他事务未提交的数据。读取其他事务未提交的数据,会造成脏读。因此在该种隔离级别下,不能解决脏读、不可重复读和幻读。


读已提交

在这种隔离级别下,所有事务只能读取其他事务已经提交的内容。能够彻底解决脏读的现象。但在这种隔离级别下,会出现一个事务的前后多次的查询中却返回了不同内容的数据的现象,也就是出现了不可重复读。这是大多数数据库系统默认的隔离级别,例如Oracle和SQL Server,但mysql不是。


可重复读

在这种隔离级别下,所有事务前后多次的读取到的数据内容是不变的。也就是某个事务在执行的过程中,不允许其他事务进行update操作,但允许其他事务进行add操作,造成某个事务前后多次读取到的数据总量不一致的现象,从而产生幻读。这是mysql的默认事务隔离级别.


可串行化

在这种隔离级别下,所有的事务顺序执行,所以他们之间不存在冲突,从而能有效地解决脏读、不可重复读和幻读的现象。但是安全和效率不能兼得,这样事务隔离级别,会导致大量的操作超时和锁竞争,从而大大降低数据库的性能,一般不使用这样事务隔离级别。



SpringBoot中使用事务


在SpringBoot中只要导入依赖就可以使用事务注解@Transactional注解完成事务。

导入依赖:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-jdbc</artifactId>

</dependency>

@Transactional

public String getTest(){

return testDao.getTest();

}


事务的传播属性(propagation):

假设有ServieA和ServiceB

ServiceA {           

void methodA() {  

         ServiceB.methodB();  

     }  

}      

ServiceB {           

void methodB() {  

     }           

}  

1.@Transactional(propagation = Propagation.REQUIRED) //默认使用REQUIRED

假设ServiceB.methodB的事务级别定义为REQUIRED

假如methodA已经起了一个事务,那么在运行methodB的时候B不会另起一个事务,这时只有外部事务并且他们是共用的,所以这时ServiceA.methodA或者ServiceB.methodB无论哪个发生异常methodA和methodB作为一个整体都将一起回滚

如果ServiceA.methodA没有事务,ServiceB.methodB就会为自己分配一个事务。这样,在ServiceA.methodA中是没有事务控制的。只是在ServiceB.methodB内的任何地方出现异常,ServiceB.methodB将会被回滚,不会引起ServiceA.methodA的回滚


2.@Transactional(propagation = Propagation.SUPPORTS)

假设ServiceB.methodB的事务级别定义为Supports

如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

如methodA有事务,那调用methodB时,methodB也有事务,如果methodA没有事务,那调用methodB时,methodB也没事务


3.@Transactional(propagation = Propagation.NOT_SUPPORTED)

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 


4.@Transactional(propagation = Propagation.MANDATORY)

使用当前的事务,如果当前没有事务,就抛出异常。

假设ServiceB.methodB的事务级别定义为MANDATORY

如果methodA没有设置事务,则调用methodB时就会抛异常。


5.@Transactional(propagation = Propagation.REQUIRES_NEW)

新建事务,如果当前存在事务,把当前事务挂起。

假设ServiceB.methodB的事务级别定义未REQUIRES_NEW,

如果methodA没有事务,则methodA调用methodB时,methodB会新建一个事务。

如果methodA有事务,那么methodA调用methodB时,methodA的事务会暂时挂起,methodB新建一个事务,若methodB抛异常,A和B的事务都会回滚,若methodB提交后methodA抛异常,只有A事务会回滚。


6.@Transactional(propagation = Propagation.NEVER)

以非事务方式执行,如果当前存在事务,则抛出异常。


7.@Transactional(propagation = Propagation.NESTED)

支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。


事务隔离级别


@Transactional(isolation = Isolation.DEFAULT)

使用数据库默认的级别,如mysql默认使用Isolation.REPEATABLE_READ(可重复读)


@Transactional(isolation = Isolation.READ_UNCOMMITTED)

未提交读,可能产生幻读、不可重复读、脏读


@Transactional(isolation = Isolation.READ_COMMITTED)

可能产生不可重复读、幻读


@Transactional(isolation = Isolation.REPEATABLE_READ)

可能产生幻读


@Transactional(isolation = Isolation.SERIALIZABLE)

使用可串形化读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容