幻读,重复读,脏读 以及Spring的事务属性说明

设置事务隔离性级别

    1)幻读:事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录
    2)不可重复读取:事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录
    3)脏读:事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。    

 

    READ UNCOMMITTED:幻读,不可重复读和脏读均允许;
    READ COMMITTED:允许幻读和不可重复读,但不允许脏读;
    REPEATABLE READ:允许幻读,但不允许不可重复读和脏读;
    SERIALIZABLE:幻读,不可重复读和脏读都不允许

 

    ORACLE默认的是 READ COMMITTED     SET TRANSACTION ISOLATION LEVEL SERIALIZABLE|READ COMMITTED|READUNCOMMITTED|REPEATABLE READ;

 

spring 中一共定义了六种事务传播属性

    PROPAGATION_REQUIRED --支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
 PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
 PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
 PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
 PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
 PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
 PROPAGATION_NESTED --如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

  前六个策略类似于EJBCMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
  它要求事务管理器或者使用JDBC 3.0 SavepointAPI提供嵌套事务行为(如Spring的DataSourceTransactionManager)

 

 

 

 

 

 

 

Spring @Transactional属性说明

 

 

 

Propagation

事务传播行为

PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

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

PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。

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

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

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

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

Isolation

事务隔离级别

@Transactional(isolation = Isolation.READ_UNCOMMITTED)读取未提交数据(会出现脏读, 不可重复读) 基本不使用

@Transactional(isolation = Isolation.READ_COMMITTED)读取已提交数据(会出现不可重复读和幻读)

@Transactional(isolation = Isolation.REPEATABLE_READ)可重复读(会出现幻读)

@Transactional(isolation = Isolation.SERIALIZABLE)串行化

 

 

 

 

 

 

 

 

 

Spring事务的传播行为

 

在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。

Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked

如果遇到checked意外就不回滚。

如何改变默认规则:

1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

 

注意: 如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。

 

spring——@Transactional事务不管理jdbc,所以要自己把jdbc事务回滚。

下面给出了回滚JDBC事务的代码示例:

Java代码
  1. public void processT(String orders) {  
  2. Context initCtx = new InitialContext();  
  3. javax.sql.DataSource ds = javax.sql.DataSource)initCtx.lookup  
  4. (“java:comp/env/jdbc/OrdersDB”);  
  5. java.sql.Connection conn = ds.getConnection();  
  6. try {  
  7. conn.setAutoCommit( false ); //更改JDBC事务的默认提交方式  
  8. orderNo = createOrder( orders );  
  9. updateOrderStatus(orderNo, “orders created”);  
  10. conn.commit(); //提交JDBC事务  
  11. } catch ( Exception e ){  
  12. try {  
  13. conn.rollback(); //回滚sJDBC事务  
  14. throw new EJBException(“事务回滚: “ + e.getMessage());  
  15. } catch ( SQLException sqle ){  
  16. throw new EJBException(“出现SQL操作错误: “ + sqle.getMessage());  
  17. }  
  18. }  

下面给出了JTA事务代码示例:

Java代码
  1. public void processOrder(String orderMessage) {  
  2. UserTransaction transaction = mySessionContext.getUserTransaction(); //获得JTA事务  
  3. try {  
  4. transaction.begin(); //开始JTA事务  
  5. orderNo = sendOrder(orderMessage);  
  6. updateOrderStatus(orderNo, “order sent”);  
  7. transaction.commit(); //提交JTA事务  
  8. } catch (Exception e){  
  9. try {  
  10. transaction.rollback(); //回滚JTA事务  
  11. } catch (SystemException se){  
  12. se.printStackTrace();  
  13. }  
  14. throw new EJBException(“事务回滚: “ + e.getMessage());  
  15. }  
  16. }  

 

 

在整个方法运行前就不会开启事务

       还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率。

       各种属性的意义:

       REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。

       NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。

       REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。

       MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。

       SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。

       NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。

       NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务 拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

 

 

 

 

 

Isolation Level(事务隔离等级)

1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行。
我们首先说并发中可能发生的3中不讨人喜欢的事情
1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。 

                            Dirty reads  non-repeatable reads phantom reads 
Serializable                 不会             不会                        不会 
REPEATABLE READ  不会             不会                        会 
READ COMMITTED   不会              会                           会 
Read Uncommitted    会                  会                           会 

readOnly
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。

猜你喜欢

转载自sunxboy.iteye.com/blog/2227273