Spring事务属性

来源:互联网 发布:java 301跳转 编辑:程序博客网 时间:2024/06/15 07:28

事务属性包括5个方面:传播行为、隔离级别、回滚规则、只读、事务超时。

1.传播行为

传播规则定义了何时要创建一个事务,或者何时使用已有的事务。

类型:
1)propagation_required
表示当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则,会启动一个新的事务;

2)propagation_requires_new
表示当前方法必须运行在自己的事务中,一个新的事务将会被启动。如果存在当前事务,那么在该方法执行期间,当前事务将会被挂起。
新启动的事务和当前事务相互独立。比如XService里有个a方法,a方法中调用b方法,如果a的事务传播属性为propagation_required,b的事务传播属性为propagation_requires_new,那么,如果b已经提交了,a中发生了异常然后回滚,b是不会回滚的;同理,如果b中发生了异常然后回滚,被a捕获,a依然可以提交它自己的事务;但是,如果b发生了异常然后回滚,把异常抛给a,a并未捕获,那么a中因为发生了异常且未捕获,如果rollback=Exception,那么a必然也发生回滚。

举个例子。p2p中用第三方支付充值成功后,第三方支付方会异步回调我们的接口,告知交易成功。我们的service方法里这样定义:

RechargeService{    @Transactional(rollbackFor = Exception.class)    rechargeSuccess(){         a(); //增加余额         try{            b(); //发充值奖励        }catch(Exception e){            //记录错误日志..        }     }    @Transactional(propagation=Propagation.REQUIRES_NEW,rollbackFor = Exception.class)    b(){      ...    }}

我们不期望发充值奖励的失败影响到整个事务的回滚,导致余额未增加。所以用了requires_new。发奖励会重新启动一个新的事务T2,b抛出异常后,T2回滚,rechargeSuccess方法捕获异常,被捕获的异常不会影响rechargeSuccess方法的事务,所以rechargeSuccess方法的事务可以正常提交。

3)propagation_nested
表示如果当前存在一个事务,那么当前方法将会在嵌套事务中运行,嵌套的事务可以独立于当前事务进行单独提交或者回滚;如果当前事务不存在,那么其行为和propagation_required一样。

以2)中a调用b方法为例,a是propagation_required,b是nested,那么b的提交或回滚要等待a,如果a成功提交了,b也会提交;如果a回滚了,那么b也要回滚。如果b发生了异常,a未捕获,那么a必然会因为发生异常而回滚。

4)propagation_mandatory
该方法必须在事务中执行,如果当前事务不存在,抛出异常;

5)propagation_never
表示当前方法不应该运行在事务上下文中,如果当前正有一个事务在运行,则抛出异常;

6)propagation_supports
表示当前方法不需要事务上下文,如果当前存在事务的话,那么这个方法将会在当前事务中运行;

7)propagation_not_supported
表示当前方法不应该在事务中运行,如果当前存在事务,那么当执行这个方法的时候,当前事务将会被挂起。

参考:
http://blog.csdn.net/paincupid/article/details/48185597
http://blog.csdn.net/klafzeng/article/details/3102307

2.隔离级别

隔离级别定义了一个事务可能受其他并发事务影响的程度。

多个事务并发执行相同的数据,可能会造成:

1)脏读(Dirty reads):脏读发生在一个事务A读取了另一个事务B改写但尚未提交的数据,如果改写在后来被回滚了,那么事务A获取的数据就是无效的。

2)不可重复读(nonrepeatable read):不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时;

3)幻读(Phantom read):它发生在一个事务T1读取了几行数据,接着另一个并发事务T2插入了一些数据,在随后的查询中,T1发现多了一些原本不存在的记录。

这三种问题的具体例子可参考:http://blog.sina.com.cn/s/blog_8020e4110101bfc6.html

下图是Spring中定义的隔离级别:

这里写图片描述

SQL标准定义了4类隔离级别:

1).Read Uncommitted(读取未提交内容)
所有事务都可以看到其他未提交事务的执行结果,即事务T1改动了数据X,但尚未提交事务,此时事务T2是可以使用这份改动的数据的。显然,如果T2稍后回滚了,那么T1就是读取了脏数据,脏读。本隔离级别很少用于实际应用,因为它的性能也不比其他隔离级别好多少。

2).Read Committed(读取已提交的内容)
一个事务只能看见已提交的事务所做的数据改变。但仍然会发生不可重复读的情况:T1先读取了字段A的值,但T1尚未提交结束,期间T2修改了A的值,T1再次读取时,发现A的值已经变了。以此类推,幻读也会发生。这是大多数数据库系统默认的隔离级别,但不是MySQL的。

3).Repeatable Read(可重读)
它确保同一事务的多次查询都会返回相同的数据,这里相同的数据指的是同一数据行的所有列的值。但不能保证T1执行期间T2插入新的数据行,T1再次查询时,发现和之前的查询相比,多了一些“幻影”行。这是MySQL的默认隔离级别。不过InnoDB通过多版本控制(MVCC,MultiVersion Concurrency Control)机制解决了这个问题。

4).Serializable(可串行化)
这是最高的隔离级别,它通过事务强制排序,使之不可能相互冲突,从而解决幻读的问题,它通常通过完全锁定事务相关的数据库表来实现的。

3.只读
如果事务只对后端的数据库进行读操作,数据库可以利用事务的只读属性进行一定的优化。因为只读优化是在事务启动的时候由数据库实施的,因此只有对那些具备启动一个新事务的传播行为(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED)的方法来说,将事务声明为只读才有意义。

4.事务超时
因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源,可以设定一个超时时间,在特定秒数后自动回滚,而不是等待其结束。

5.回滚规则
Spring默认情况下,事务只有在遇到运行期异常RuntimeException的时候才会回滚,而在遇到检查型异常时不会回滚。但是你可以声明事务在遇到特定的检查型异常时会像运行期异常那样回滚。

0 0
原创粉丝点击