总览:spring
原子性(Atomicity)
原子性是指事务包含的全部操做要么所有成功,要么所有失败回滚,这和前面两篇博客介绍事务的功能是同样的概念,所以事务的操做若是成功就必需要彻底应用到数据库,若是操做失败则不能对数据库有任何影响。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另外一个一致性状态,也就是说一个事务执行以前和执行以后都必须处于一致性状态。
拿转帐来讲,假设用户A和用户B二者的钱加起来一共是5000,那么无论A和B之间如何转帐,转几回帐,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,好比操做同一张表时,数据库为每个用户开启的事务,不能被其余事务的操做所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始以前就已经结束,要么在T1结束以后才开始,这样每一个事务都感受不到有其余事务在并发地执行。
关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即使是在数据库系统遇到故障的状况下也不会丢失提交事务的操做。数据库
脏读
两个事务正在并发的执行,事实上最后结果应该是1500才对,时间5时刻的查询余额为0就是脏数据,事务A读取了事务B中未提交的数据,这就是脏读。并发
时间 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | - |
2 | - | 开始事务 |
3 | - | 查询余额有1000 |
4 | - | 取出1000,余额0 |
5 | 查询余额0 | - |
6 | - | 撤销掉事务 |
7 | 存入500,余额500 | - |
8 | 提交事务 | - |
不可重复读
两个事务正在并发的执行,结果A两次读取的结果不同,这是由于两次查询有间隔,期间被其余事务修改并提交了事务,相比脏读的区别是,不可重复读是读取另外一事务提交的数据。这种现象也是正常的,是因为事务的隔离级形成的,可是在在某些特别的状况下也是不容许的。this
时间 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | - |
2 | - | 开始事务 |
3 | - | 查询余额有1000 |
4 | 查询余额1000 | - |
5 | 取出1000,余额0 | |
6 | - | 提交事务 |
7 | 查询余额0 | - |
幻读
两个事务正在并发的执行,事务A第一次统计和第二统计的结果不同,是由于事务B新增了一条数据,和不可重复读同样,都是读取了另一个事务的数据,不一样的是不可重复读查询的是同一条数据,而幻读则是针对批量的数据,或者说不可重复读是A读取了B的更新数据,幻读是A读取了B的新增数据。spa
时间 | 事务A | 事务B |
---|---|---|
1 | 开始事务 | - |
2 | - | 开始事务 |
3 | 统计总金额10000 | - |
4 | - | - |
5 | 存入100 | |
6 | - | 提交事务 |
7 | 统计总金额10100 | - |
明白上面的问题以后就明白为何须要隔离级别了,不一样的隔离级别能处理不一样的并发事务问题,下表:3d
事务级别 | 脏读 | 不可重复读 | 幻觉读 |
---|---|---|---|
READ_UNCOMMITTED | 容许 | 容许 | 容许 |
READ_COMMITTED | 禁止 | 容许 | 容许 |
REPEATABLE_READ | 禁止 | 禁止 | 容许 |
SERIALIZABLE | 禁止 | 禁止 | 禁止 |
MySQL默认的事务级别是REPEATABLE_READcode
JDBC | 访问 | |
---|---|---|
TRANSACTION_READ_UNCOMMITTED | ur | 就是俗称“脏读”(dirty read),在没有提交数据时可以读到已经更新的数据 |
TRANSACTION_READ_COMMITTED | cs | 在一个事务中进行查询时,容许读取提交前的数据,数据提交后,当前查询就能够读取到数据。update数据时候并不锁住表 |
TRANSACTION_REPEATABLE_READ | rs | 在一个事务中进行查询时,不容许读取其余事务update的数据,容许读取到其余事务提交的新增数据 |
TRANSACTION_SERIALIZABLE | rr | 在一个事务中进行查询时,不容许任何对这个查询表的数据修改。 |
事务怎么传播?方法A传播到方法B。
Spring解决的就是方法之间的事务传播。
下面看下每一种行为具体表明的含义:blog
业务方法须要在一个事务中运行。若是方法运行时,已经处在一个事务中,那么这个时候就会加入到该事务中,若是当前没有事务环境的话,就会为本身建立一个新的事务。图片
这一事务属性代表,若是业务方法A在某个事务范围内被调用,则方法成为事务的一部分。若是业务方法在事务范围外被调用,则方法在没有事务的环境下执行。即当标注了事务传播属性——SUPPORTS的业务方法在另外一个bean的业务方法中执行时,若是另外一个bean的业务方法开启了事务,它就会处在事务中执行,若是另外一个bean的业务方法也没开启事务,那么它也在没有事务的环境中进行。事务
该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起本身的事务。若是业务方法在没有事务的环境下调用,容器就会抛出异常。一种比较强硬的方式。
该属性代表无论当前是否存在事务,业务方法总会为本身发起一个新的事务。若是方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被建立,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。
声明方法不须要事务。若是方法没有关联到一个事务,容器不会为它开启事务。若是方法在一个事务中被调用(在其余业务bean的方法中被调用了,而其余业务bean的方法是开启了事务的),该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
指定业务方法绝对不能在事务范围内执行。若是业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行。比较强硬的方式,就是不支持事务。
(嵌套事务)若是一个活动的事务存在,则当前方法运行在一个嵌套的事务中。 若是没有活动事务,就建立一个新的事务。它使用了一个单独的事务,这个事务拥有多个能够回滚的保存点。内部事务的回滚不会对外部事务形成影响。外部事务回滚会致使内部事务的回滚。若是被调用的内部方法没有捕获异常,跑出异常也会致使外部事务的回滚。
看下Spring中枚举定义的7种事务传播行为
package org.springframework.transaction.annotation; public enum Propagation { REQUIRED(0), SUPPORTS(1), MANDATORY(2), REQUIRES_NEW(3), NOT_SUPPORTED(4), NEVER(5), NESTED(6); private final int value; private Propagation(int value) { this.value = value; } public int value() { return this.value; } }
在使用注解方式的事务时候咱们能够用下面的方式来设置事务的传播行为
@Transactional(propagation = Propagation.REQUIRED)
是否是很方便。
readOnly属性:设置为只读事务,对于只读事务,它就不能进行更新操做,通常只存在数据读取的时候,能够将readOnly属性设置为true,可提供效率。
timeout属性:表明事务的超时时间,默认为30s,通常状况下都不须要设置超时时间。若是超过期间就回滚。