spring事物传播特性--数据库的隔离级别


1.spring事物传播特性
================================================
**PROPAGATION_REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中。(这是最常见的选择)

**PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。

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

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

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

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

**PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED 类似的操作。


2.数据库的隔离级别
------------------------------------------
为了兼顾并发效率和异常控制,在标准SQL规范中,定义了4个事务隔
离级别,(ORACLE和SQLSERER对标准隔离级别有不同的实现)

##Read Uncommitted:
直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别
的事务可以读到这个改变.这是很不安全的.

##Read Committed:
直译就是"读提交",意思就是语句提交以后,即执行了COMMIT以后
别的事务才能读到这个改变. (行级锁,不锁间隙)

##Repeatable Read:
直译就是"可以重复读",这是说在同一个事务里面先后执行同一个
查询语句的时候,得到的结果是一样的. (行级锁且是锁间隙)

##Serializable:
直译就是"序列化",意思是说这个事务执行的时候不允许别的事务
并发执行.


并发问题
-----------------------------------------
由于并发操作带来的数据不一致性包括:丢失数据修改、读”脏”数据(脏读)、不可重复读、产生幽灵数据。

(1)丢失数据修改
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。
每个事务都不知道其它事务的存在。最后的更新将覆盖由其它事务所做的更新,这将导致数据丢失。

(2)读“脏”数据(脏读)
读“脏”数据是指事务T1修改某一数据,并将其写回磁盘,事务T2读取同一数据后,T1由于某种原因撤回修改,
T2读到的数据与数据库的数据不一致,则T2读到的数据就为“脏”数据,即不正确的数据。

(3)不可重复读
指事务T1读取数据后,事务T2执行更新操作,使T1无法读取前一次结果。

(4)产生幽灵数据
按一定条件从数据库中读取了某些记录后,T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现某些记录消失;
T1按一定条件从数据库中读取某些数据记录后,T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。

====================================================================================

Spring框架的事务机制将默认的只在抛出unchecked exceptions时才标识事务回滚。
从事务方法中抛出的Checked exceptions将不被标识进行事务回滚。

Unchecked Exception:包括Error与RuntimeException. 这类异常都是RuntimeException的子类(RuntimeException继承自Exception)。

Checked Exception:除了Error与RuntimeException,其它剩下的异常. 这类异常都是Exception的子类;
                   编译时在语法上必须处理的异常,因此必须在语法上以try..catch加以处理。

注:事务方法中,try..catch的代码,不参加事务,要想参加事务,需要在catch块中手动抛出运行时异常。
例:
1.事务将提交
saveUser(){//受事务控制
    try{
        dao.insert(user);
        6 / 0;
    }catch(e){
        syso("异常");
    }
    dao.update(user2);
}
2.事务回滚
saveUser(){//受事务控制
    try{
        dao.insert(user);
        6 / 0;
    }catch(e){
        syso("异常");
        throw RuntimeException;//需要在这里抛出运行时异常
    }
    dao.update(user2);

}

===============================================================

转自:http://blog.csdn.net/fg2006/article/details/6937413

数据库事务的隔离级别有4个,由低到高依次为Read uncommittedRead committedRepeatable readSerializable,这四个级别可以逐个解决脏读不可重复读幻读这几类问题。

√: 可能出现    ×: 不会出现

脏读不可重复读幻读
Read uncommitted
Read committed×
Repeatable read××
Serializable×××

 

注意:我们讨论隔离级别的场景,主要是在多个事务并发的情况下,因此,接下来的讲解都围绕事务并发。

Read uncommitted 读未提交

公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000元整,非常高兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有2000元,singo空欢喜一场。


 

出现上述情况,即我们所说的脏读,两个并发的事务,“事务A:领导给singo发工资”、“事务B:singo查询工资账户”,事务B读取了事务A尚未提交的数据。

当隔离级别设置为Read uncommitted时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。

Read committed 读提交

singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为何......

出现上述情况,即我们所说的不可重复读,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。

当隔离级别设置为Read committed时,避免了脏读,但是可能会造成不可重复读。

大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。如何解决不可重复读这一问题,请看下一个隔离级别。

Repeatable read 重复读

当隔离级别设置为Repeatable read时,可以避免不可重复读。当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。

虽然Repeatable read避免了不可重复读,但还有可能出现幻读

singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction ... ),并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出现了幻觉,幻读就这样产生了。

注:MySQL的默认隔离级别就是Repeatable read。

Serializable 序列化

Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。(锁整张表)


注意:

可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了;

幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样。

比如可重复读:一个事务的两次查询操作过程,不允许对该记录进行update操作,但是可以插入或者删除数据;

select * from t_user where name like '%和%';该语句查询第一次后,

执行insert into t_user(name) values('中和');

再执行时,结果跟第一次不一样,幻读;


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值