Spring学习笔记(十六)-----Spring in Action

Spring学习笔记(十六)-----Spring in Action

    在软件中,要么全有要么全无的操作成为事务。事务允许你把几个操作组成一个单一的工作单元,这个工作单元要么全部发生要么全部不发生。如果每件事都顺利,那么这个事务是成功的。但是如果任何一件事情出错的话,那么已经发生的行为就被清除掉,就像什么事情都没发生一样。
    Spring对事务管理有丰富的支持,程序控制的和声明式的。
    原子性(Atomic):事务由一个或多个行为绑定在一起组成,好像是一个单独工作单元。原子性确保在十五中的所有操作要么都发生,要么都不发生。
    一致性(Consistent):一旦一个事务结束了(不管成功失败),系统所处的状态和它的业务规则是一致的。就是说数据应当不会被破坏。
    隔离性(Isolated):事务应该允许多个用户操作同一数据,一个用户的操作不会和其他用户的操作相混淆。因此,事务必须是互相隔离的,防止并发读写同一数据的情况发生。
    持久性(Durable):一旦事务完成,事务的结果应该持久化,这样不管什么样的系统崩溃,他们都将幸免于难。
 
    Spring对程序控制事务管理的支持和EJB的有很大不同。EJB的事务管理和JTA密不可分,和EJB不同的是,Spring使用了一种回调机制,把真实的事务实现从事务代码中抽象出来。选择程序控制事务管理还是声明式事务管理,很大程度上是在细粒度控制与简便操作之间做出决定。当你在代码中编写事务时,你能精确控制事务的边界,在你希望的地方精确的开始和结束。典型的情况下,你不需要程序控制事务所提供的细粒度控制,你会选择在上下文定义文件中声明你的事务。

    Spring对声明式事务管理的支持是通过它的AOP框架实现的。这样做是非常自然的,因为事务是系统级的,凌驾于应用的主要功能之上的。

    在Spring里,事务属性是对事务策略如何应用到方法的描述。这个描述包括:传播行为、隔离级别、只读提示、事务超时间隔
    传播行为:
    PROPAGATION_MANDATORY:表示该方法必须运行在一个事务中。如果当前事务不存在,将抛出一个异常。
    PROPAGATION_NESTED:表示如果当前已经存在一个事务,则该方法应当运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交或回滚。如果当前事务不存在,那么它看起来和PROPAGATION_REQUIRED没有两样。
    PROPAGATION_NEVER:表示当前的方法不应该运行在一个事务上下文中。如果当前存在一个事务,则会抛出一个异常。
    PROPAGATION_NOT_SUPPORTED:表示该方法不应在事务中运行。如果一个现有的事务正在运行中,它将在该方法的运行期间被挂起。
    PROPAGATION_REQUIRED:表示当前方法必须运行在一个事务中。如果一个现有的事务正在运行中,该方法将运行在这个事务中。否则的话,要开始一个新的事务。
    PROPAGATION_REQUIRES_NEW:表示当前方法必须运行在它自己的事务里。它将启动一个新的事务。如果有事务运行的话,将在这个方法运行期间被挂起。
    PROPAGATION_SUPPORTS:表示当前方法不需要事务处理环境,但如果有一个事务已经在运行的话,这个方法也可以在这个事务里运行。

   传播规则回答了一个问题:就是新的事务是否要被启动或是被挂起,或者方法是否要在事务环境中运行。

   隔离级别:在一个典型的应用中,多个事务并发运行,经常会操作同一个数据来完成它们的任务。并发,虽然是必须的,但会导致下面问题:
1、脏读:脏读发生在一个事务读取了被另一个事务改写但还未提交的数据时。如果这些改变在稍后被回滚,那么第一个事务读取的数据就是无效的。
2、不可重复读:不可重复读发生在一个事务执行相同的查询2次或2次以上,但每一次查询结果都不同时。这通常是由于另一个并发事务在2次查询之间更新了数据。
3、幻读:幻读和不可重复读相似。当一个事务读取几行纪录后,另一个并发事务插入一些记录,幻读就发生了。隔离级别有如下几个:
ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED:允许你读取还未提交的改变了的数据,可能导致脏读、幻读、不可重复读
ISOLATION_READ_COMMITTED:允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可能发生。
ISOLATION_REPEATABLE_READ:对相同字段的多次读取的结果是一致的,除非数据被事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
ISOLATION_SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻读。这在所有隔离级别中也是最慢的。

    只读:如果一个事务只对后端是据库执行读操作,数据库就可能利用事务只读的特性,使用某些优化措施。通过声明一个事务为只读,你就给了后端数据库一个机会,来应用那些它认为合适的优化措施。因为只读的优化措施是在事务启动时由后端数据库实施的,所以,只有将那些具有可能启动新事务的传播行为的方法的事务标记成只读才有意义(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED) TransactionProxyFactoryBean参照一个方法的事务属性,决定如何在那个方法上执行事务策略。

<? xml version="1.0" encoding="UTF-8" ?>
< beans
    
xmlns ="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >
    
< bean  id ="dataSource"  class ="org.springframework.jndi.JndiObjectFactoryBean" >
        
< property  name ="jndiName" >
            
< value > java:comp/env/jdbc/myDatasource </ value >
        
</ property >
    
</ bean >
    
< bean  id ="transactionManager"  class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
        
< property  name ="dataSource" >
            
< ref  bean ="dataSource" />
        
</ property >
    
</ bean >
    
<!--  这个对象有一个值为courseService的id.当应用从应用上下文里请求一个courseService时,它将得到一个被
    TransactionProxyFactoryBean包裹的实例。 
-->
    
< bean  id ="courseService"  class ="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
        
<!--  代理所实现的接口  -->
        
< property  name ="proxyInterfaces" >
            
< list >
                
< value >
                    com.springinaction.training.service.CourseService
                
</ value >
            
</ list >
        
</ property >
        
<!--  被代理的对象  -->
        
< property  name ="target" >
            
< ref  bean ="courseServiceTarget" />
        
</ property >
        
<!--  事务管理器  -->
        
< property  name ="transactionManager" >
            
< ref  bean ="transactionManager" />
        
</ property >
        
<!--  事务的属性源  -->
        
< property  name ="transactionAttributeSource" >
            
< ref  bean ="transactionAttributeSource" />
        
</ property >
    
</ bean >
    
<!--  要知道尽管可以改变MatchAlwaysTransactionAttributeSource的事务属性参数,但它总是返回相同的事务属性,而
    不关心参与交易的哪一个方法。当你有一个相对简单的应用,把同样的事务策略应用到所有方法都没问题时,使用MatchAlwaysT
    ransactionAttributeSource就相当好。但是,在那些更为复杂的应用中,你很可能需要对不同的方法应用不同的事务策略。在那样
    情况下,你需要在应用何种策略的问题上做更多精确的控制。 
-->
    
< bean  id ="transactionAttributeSource"  class ="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource" >
        
< property  name ="transactionAttribute" >
            
< ref  bean ="myTransactionAttribute" />
        
</ property >
    
</ bean >
    
<!--  定义事务策略  -->
    
< bean  id ="myTransactionAttribute"  class ="org.springframework.transaction.interceptor.DefaultTransactionAttribute" >
        
<!--  传播行为  -->
        
< property  name ="propagationBehaviorName" >
            
< value > PROPAGATION_REQUIRES_NEW </ value >
        
</ property >
        
<!--  隔离级别  -->
        
< property  name ="isolationLevelName" >
            
< value > ISOLATION_REPEATABLE_READ </ value >
        
</ property >
    
</ bean >
</ beans >

 

除了将transactionAttributeSource对象织入到TransactionProxyFactoryBean的transactionAttributeSource属性中外,还有一种简单的方法。发展到现在,TransactionProxyFactoryBean也有一个transactionAttributes属性为transactionProperties.

<? xml version="1.0" encoding="UTF-8" ?>
< beans
    
xmlns ="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" >
    
< bean  id ="dataSource"  class ="org.springframework.jndi.JndiObjectFactoryBean" >
        
< property  name ="jndiName" >
            
< value > java:comp/env/jdbc/myDatasource </ value >
        
</ property >
    
</ bean >
    
< bean  id ="transactionManager"  class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
        
< property  name ="dataSource" >
            
< ref  bean ="dataSource" />
        
</ property >
    
</ bean >
    
<!--  这个对象有一个值为courseService的id.当应用从应用上下文里请求一个courseService时,它将得到一个被
    TransactionProxyFactoryBean包裹的实例。 
-->
    
< bean  id ="courseService"  class ="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
        
<!--  代理所实现的接口  -->
        
< property  name ="proxyInterfaces" >
            
< list >
                
< value >
                    com.springinaction.training.service.CourseService
                
</ value >
            
</ list >
        
</ property >
        
<!--  被代理的对象  -->
        
< property  name ="target" >
            
< ref  bean ="courseServiceTarget" />
        
</ property >
        
<!--  事务管理器  -->
        
< property  name ="transactionManager" >
            
< ref  bean ="transactionManager" />
        
</ property >
        
<!--  事务的属性源  -->
        
< property  name ="transactionAttributeSource" >
            
< ref  bean ="transactionAttributeSource" />
        
</ property >
    
</ bean >
    
<!--  NameMatchTransactionAttributeSource的properties属性把方法名映射到事务属性描述器上。注意CourseException
    用一个负号标记。异常可以用负号或正号标记,当负号异常抛出时,将触发回滚;相反的,正号异常表示事务仍可提交,即使这个异常抛出 
-->
    
< bean  id ="transactionAttributeSource"  class ="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource" >
        
< property  name ="properties" >
            
< props >
                
< prop  key ="enrollStudentInCourse" >
                    PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ,readOnly,
                    -CourseException
                
</ prop >
                
<!--  还可以使用通配符  -->
                
< prop  key ="get*" >
                    PROPAGATION_SUPPORTS
                
</ prop >
            
</ props >
        
</ property >
    
</ bean >
</ beans >

 

 

你可能感兴趣的:(Spring学习笔记(十六)-----Spring in Action)