一、事务的传播特性
REQUIRED:默认的传播特性,业务方法需要在一个事务中运行,如果一个方法已经处在一个事务中那么就加入到这个事务中,否则就会创建一个事务。
NEVER:指定的业务方法绝对不能在事务范围内运行,如果业务方法在某个事务中执行,就会抛异常,只有业务方法没有任何事务才正常执行。
MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能自己发起自己的事务,如果业务方法不存在事务,容器就抛异常。
SUPPORTS:如果业务方法中已经在某个事务中被调用,则方法就称为事务的一部分,如果外部业务方法没有开启事务,supports该方法也会在没有事务的环境中执行。
在没有事务的环境下运行
NOT_SUPPORTED:如果该业务方法在一个事务中被调用,那么当前的事务会被挂起,执行该业务方法,方法执行完毕唤醒被挂起的事务,如果业务方法不在一个事务中执行,该方法也不会开事务。不管是否在有无事务的环境中执行都不开启事务。
REQUIRES_NEW:不管是否存在事务,业务方法总会自己开启一个事务,如果在已有事务的环境中调用,已有事务会被挂起,新的事务会被创建,直到业务方法调用结束,已有事务才被唤醒
NESTED:如果业务方法在一个事务中执行,就在这个事务中嵌套,如果没有事务按着required执行,开启单独的事务,这种事务有多个事务的保存点,内部事务的回滚对外部事务没有影响
二、事务的并发
并发问题:
问题1:脏读(dirty read)
A事务读到B事务没有提交的数据,并且A来修改这个数据,如果恰巧B做事务回滚,那么A事务读到的数据就是错误的
时间 |
转账事务A |
取款事务B |
T1 |
|
开启事务 |
T2 |
开启事务 |
|
T3 |
|
查询余额1000元 |
T4 |
|
取500,剩500 |
T5 |
查询账户剩500(脏读) |
|
T6 |
|
撤销了取款把余额恢复到1000 |
T7 |
汇入100,余额变成600 |
|
T8 |
提交事务 |
问题2:不可重复读(unrepeatable read)
指的是A事务读取了B事务已经提交了的更改数据,假设A取款的过程中B向账户汇入100,A事务两次读取数据不一致
时间 |
转账事务A |
取款事务B |
T1 |
|
开启事务 |
T2 |
开启事务 |
|
T3 |
|
查询余额1000元 |
T4 |
查询余额1000元 |
|
T5 |
|
取出100,余额900 |
T6 |
|
事务的提交 |
T7 |
查询余额900元(和T4一样) |
|
T8 |
提交事务 |
问题3:幻读(phantom read)
A事务读取B事务新增的数据,假设银行做在一个A事务中统计,在统计过程中B新增了用户,A的事务中两次统计不同
时间 |
统计金额A |
开户事务B |
T1 |
|
开启事务 |
T2 |
开启事务 |
|
T3 |
统计为10000 |
|
T4 |
|
新增用户,并且存入100 |
T5 |
|
|
T6 |
|
事务的提交 |
T7 |
统计为10100 |
|
T8 |
提交事务 |
问题4:第一类更新丢失
时间 |
转账事务A |
取款事务B |
T1 |
|
开启事务 |
T2 |
开启事务 |
|
T3 |
|
查询余额1000元 |
T4 |
查询余额1000元 |
|
T5 |
汇入100,余额变1100 |
|
T6 |
提交 |
|
T7 |
|
取出100,余额变成900 |
T8 |
|
撤销事务 |
T9 |
|
余额要恢复1000(更新丢失) |
问题5:第二类更新丢失
时间 |
转账事务A |
取款事务B |
T1 |
|
开启事务 |
T2 |
开启事务 |
|
T3 |
|
查询余额1000元 |
T4 |
查询余额1000元 |
|
T5 |
|
取100,余额900 |
T6 |
|
提交 |
T7 |
汇入100,余额变1100 |
|
T8 |
提交事务 |
|
T9 |
余额变1100(丢失更新) |
隔离级别 |
脏读 |
不可重复读 |
幻读 |
第一丢失更新 |
第二丢失更新 |
READ UNCOMMITED |
Y |
Y |
Y |
N |
Y |
READ COMMITED |
N |
Y |
Y |
N |
Y |
REPEATABLE READ |
N |
N |
Y |
N |
N |
SERIALIZEABLE |
N |
N |
N |
N |
N |