现在的位置: 首页 > 综合 > 正文

Spring事务属性详解

2013年08月02日 ⁄ 综合 ⁄ 共 4530字 ⁄ 字号 评论关闭

Spring,是一个Java开源框架,是为了解决企业应用程序开发复杂性由Rod Johnson创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。


Spring声明式事务让我们从复杂的事务处理中得到解脱,使得我们再也不必去处理获得连接、关闭连接、事务提交和回滚等这些操作,再也无需我们在与事务相关的方法中处理大量的try…catch…finally代码。 
我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为、事务的隔离级别、事务的超时值、事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。 

Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager(github)使用,PlatfromTransactionManager是spring事务管理的核心接口。


TransactionDefinition.javaspring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java,github

public interface TransactionDefinition {
    int getPropagationBehavior();
    int getIsolationLevel();
    int getTimeout();
    boolean isReadOnly();
}

1) getPropagationBehavior(): 返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。 
2) getTimeout(): 它返回事务必须在多少秒内完成。 
3) isReadOnly(): 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。 
4) getIsolationLevel(): 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。 

在TransactionDefinition接口中,定义了五个不同的事务隔离级别 :
1) ISOLATION_DEFAULT 

这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应 

2) ISOLATION_READ_UNCOMMITTED 

这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 
  例如: 
  Mary的原工资为1000,财务人员将Mary的工资改为了8000,但未提交事务 

  1. Connection con1 = getConnection();  
  2. con.setAutoCommit(false);  
  3. update employee set salary = 8000 where empId ="Mary";  

与此同时,Mary正在读取自己的工资 

  1. Connection con2 = getConnection();  
  2. select  salary from employee where empId ="Mary";  
  3. con2.commit();  


Mary发现自己的工资变为了8000,欢天喜地! 
而财务发现操作有误,而回滚了事务,Mary的工资又变为了1000 

  1. //con1  
  2.   con1.rollback();  

像这样,Mary记取的工资数8000是一个脏数据。 
ISOLATION_READ_COMMITTED  保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 

ISOLATION_REPEATABLE_READ  这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 

在事务1中,Mary 读取了自己的工资为1000,操作并没有完成 

  1. con1 = getConnection();  
  2. select salary from employee empId ="Mary";  


在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务. 

  1. con2 = getConnection();  
  2. update employee set salary = 2000;  
  3. con2.commit();  


在事务1中,Mary 再次读取自己的工资时,工资变为了2000 

  1. //con1  
  2. select salary from employee empId ="Mary";  


在一个事务中前后两次读取的结果并不致,导致了不可重复读。 
使用ISOLATION_REPEATABLE_READ可以避免这种情况发生。 

ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。 

目前工资为1000的员工有10人。 
事务1,读取所有工资为1000的员工。 

  1. con1 = getConnection();  
  2. Select * from employee where salary =1000;  

共读取10条记录 

这时另一个事务向employee表插入了一条员工记录,工资也为1000 

  1. con2 = getConnection();  
  2. Insert into employee(empId,salary) values("Lili",1000);  
  3. con2.commit();  


事务1再次读取所有工资为1000的员工 

  1. //con1  
  2. select * from employee where salary =1000;  


共读取到了11条记录,这就产生了幻像读。 
ISOLATION_SERIALIZABLE能避免这样的情况发生。但是这样也耗费了最大的资源。 

getPropagationBehavior()返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。 
在TransactionDefinition接口中定义了七个事务传播行为。 
PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 

  1. //事务属性 PROPAGATION_REQUIRED  
  2. methodA{  
  3. ……  
  4. methodB();  
  5. ……  
  6. }  
  7.   
  8. //事务属性 PROPAGATION_REQUIRED  
  9. methodB{  
  10.    ……  
  11. }  


使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。 

单独调用methodB方法 

  1. main{  
  2.   metodB();  
  3. }  

相当于 

  1. Main{  
  2. Connection con=null;  
  3.   
  4.    rry{  
  5.       con = getConnection();  
  6.       con.setAutoCommit(false);  
  7. //方法调用  
  8. methodB();  
  9. //提交事务  
  10. con.commit();  
  11. }  
  12. Catch(RuntimeException ex){  
  13.   //回滚事务  
  14.   con.rollback();    
  15. }  
  16. finally{  
  17.   //释放资源  
  18.   closeCon();  
  19. }  
  20. }  


Spring保证在methodB方法中所有的调用都获得到一个相同的连接。在调用methodB时,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务。 

单独调用MethodA时,在MethodA内又会调用MethodB. 

执行效果相当于 

  1. main{  
  2.    Connection con = null;  
  3.    try{  
  4.       con = getConnection();  
  5.       methodA();  
  6.       con.commit();  
  7. }  
  8. cathc(RuntimeException ex){  
  9.  con.rollback();  
  10. }  
  11. finally{  
  12.   closeCon();  
  13. }   
  14. }  


调用MethodA时,环境中没有事务,所以开启一个新的事务. 
当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务。 

PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
 

  1. //事务属性 PROPAGATION_REQUIRED   
  2. methodA(){  
  3.   methodB();  
  4. }  
  5.   
  6. //事务属性 PROPAGATION_SUPPORTS   
  7. methodB(){  
  8.   ……  
  9. }  


单纯的调用methodB时,methodB方法是非事务的执行的。 
当调用methdA时,methodB则加入了methodA的事务中,事务地执行。 

PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。 

  1. //事务属性 PROPAGATION_REQUIRED   
  2. methodA(){  
  3.   methodB();  
  4. }  
  5.   
  6. //事务属性 PROPAGATION_MANDATORY   
  7. methodB(){  
  8.   ……  
  9. }  


当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常 
throw new IllegalTransactionStateException("Transaction propagation 'mandatory' but no existing transaction found"); 

当调用methodA时,methodB则加入到methodA的事务中,事务地执行。 

PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。 

  1. //事务属性 PROPAGATION_REQUIRED   
  2. methodA(){  
  3.   doSomeThingA();  
  4. methodB();  
  5. doSomeThingB();  
  6. }  
  7.   
  8. //事务属性 PROPAGATION_REQUIRES_NEW   

抱歉!评论已关闭.