Spring学习笔记---6-Spring事务管理(2)

来源:互联网 发布:方正字体投诉淘宝店铺 编辑:程序博客网 时间:2024/05/19 13:08


一、编程式事务管理


       Spring 在项目中使用编程式事务管理是很少的,基本上是不提倡的,因为它违反了Spring提出AOP思想的设计理念。不过我们还是来看看编程式事务管理的逻辑来更好的理解事务管理。
     
       编程式事务管理提供了模板类org.springframework.transaction.support.TransactionTemplate(在spring-tx.jar包下),以满足一些特殊场合的需要。
       
       TransactionTemplate 和那些持久化模板类一样,是线程安全的(1、如何知道他是线程安全的),因此,可以在多个业务类中共享(2、HOW)TransactionTemplate 实例进行事务管理。 

       我看了一眼TransactionTemplate.java这个类的方法:
        
                  
         我就不全粘贴了, 只看重要的:setTransactionManager()--设置事务管理器。
        由于Spring事务管理基于TransactionSychrizationManager进行工作,所以如果在回调接口方法中需要显式访问底层数据连接,必须通过资源获取工具类得到线程绑定的数据连接,这是Spring事务管理的底层协议,不容违反。如果ViewSpaceDao是基于Spring提供的模板类构建的,由于模板类已经在内部使用了资源获取工具类获取数据连接,所以用户就不必关心底层数据连接的获取问题了。
    
        先来回答第一个问题:1、何知道TransactionTemplate 是线程安全的?
        答:正如在下面所说:Spring事务管理基于TransactionSychrizationManager进行工作;如果看下面这个图,TransactionSychrizationManager这个类就可以看到,它是利用ThreadLocal来进行事务的线程管理,ThreadLocal本身是线程安全的,而且从TransactionSychrizationManager的字面意思就可以看到,它应该就是线程安全的。
                                                  
 
        第二个问题: 2、HOW,如何共享其?
        答:对于线程的内部机制不熟悉,需要接着学习,但是对于线程的共享,这个是对的,怎么做到,可以参考多线程的相关资料,这一方法有时间,研究完会附上来。


二、Spring的事务处理

       2.1 用XML 实现声明式事务
  
       大多数Spring用户选择声明式事务管理的功能,这种方式对代码的侵入性最小,可以让事务管理代码完全从业务代码中抽离,非常符合非侵入式的轻量级容器的理念。
        Spring的声明式事务管理是通过SpringAOP实现的,通过事务的声明性信息,Spring负责将事务管理增强逻辑动态织入业务方法相应连接点中。这些逻辑包括获取线程绑定资源,开始事务,提交或者回滚事务,进行一场转换和处理等工作。
        
        回滚规则的概念非常重要:它使用户能够指定什么样的异常导致自动回滚,什么样的异常不影响事务提交,这些规则可以在配置文件中通过声明的方式指定,同时,用户仍旧可以通过调用TransactionStatus.setRollBackOnly()方法编程式的回滚当前事务。通常,定义一条规则,如声明MyApplicationException必须总是导致事务回滚。这种方式带来了显著的好处,它使用户的业务对象不必依赖于事务实施。典型的例子是用户不必在代码中导入SpringAPI、事务代码等。

       
       通常在项目的接口中,我们为了实现一个功能,往往需要先后执行几条sql语句,如:
           
           下面是其接口实现类:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
       在上面的接口中需要调用5个方法,分别执行每个方法中对数据库的操作,而这是一个业务功能,如果其中有一个执行不成功,则事务回滚到初始状态。

       ViewSpaceImpl 是一个POJO,只是简单实用持久层的多个DAO类,通过他们的协作实现IViewSpace接口的功能。在这里看不到任何事务操作的代码,所以如果直接使用ViewSpaceImpl,这些方法都将以无事务的方式运行。现在,我们的任务是通过Spring声明事务配置让这些业务方法拥有合适的事务功能。       
 
       注解:上面的代码是业务代码,为了实现一个业务功能,常常会执行好几个sql,相应的sql组合为一个事务。

       在Spring早起版本中,用户必须通过TransactionProxyFactoryBean代理类对需要事务管理的业务类进行代理,以便实施事务功能的增强。在Spring 3.0 中年,通过该代理类实施声明式事务的方式不再被推荐,但它依然是可用的。

声明式事务配置

       从循序渐进的学习角度来看,了解TransactionProxyFactoryBean 有助于我们更直观的理解Spring实施声明性事务的内部工作原理。所以我们可以将学习这个代理类作为学习Spring事务管理的切入点。
        下面我们来学习Spring3.0新的配置方式:
          
            
            
        在上面配置中看到一行:<import resource="classpath:applicationContext-dao.xml"/> 这句话的意思是:导入这个文件,该文件负责配置DAO Bean、资源池等基础设施,这些信息统一在applicationContext-dao.xml配置文件中,其具体代码实例如下:
              

       如上图可看到该实例使用了Spring JDBC的持久化技术,所以它的资源池是JDBC数据源,而事务管理器是DataSourceTransactionManager(看声明式事务配置的下面第一幅图中 -1 )。如果使用其他持久化技术,就需要对基础设施和事务管理器进行相应的调整。
       按照约定的习惯,需要事务增强的业务类一般将id取名为xxxTarget,如 上上图 -2 ;这可以在字面上表示该Bean是要被代理的目标Bean。

       通过TransactionProxyFactoryBeab 对业务员类进行代理,织入事务增强功能,如 -1  所示。 
       首先,需要为该代理类指定事务管理器,这些事务管理器实现了PlatformTransactionManager 接口;
       其次,通过target属性指定需要代理的目标Bean;
       最后,为业务Bean 的不同方法配置事务属性。
       Spring 允许通过键值配置业务方法的事务属性信息,键可以使用通配符,如get* 代表类中所有以get 为前缀的方法,它匹配 IViewSpace的getViewSpace(int spaceid) 和 getViewSpaceNum()方法,而key="*" 匹配IViewSpace接口的所有的方法。

       在上面的事务配置的文件中,事务传播属性是必须要填写的。
       spring的事务传播行为 
       spring事务的传播行为说的是当一个方法调用另一个方法时,事务该如何操作。 
        PROPAGATION_MANDATORY:该方法必须运行在一个事务中。如果当前事务不存在则抛出异常。 
        PROPAGATION_NESTED:如果当前存在一个事务,则该方法运行在一个嵌套的事务中。被嵌套的事务可以从当前事务中单独的提交和回滚。如果当前不存在事务,则开始一个新的事务。各厂商对这种传播行为的支持参差不齐,使用时需注意。 
        PROPAGATION_NEVER:当前方法不应该运行在一个事务中。如果当前存在一个事务,则抛出异常。 
        PROPAGATION_NOT_SUPPORTED:当前方法不应该运行在一个事务中。如果一个事务正在运行,它将在该方法的运行期间挂起。 
        PROPAGATION_REQUIRED:该方法必须运行在一个事务中。如果一个事务正在运行,该方法将运行在这个事务中。否则,就开始一个新的事务。 
        PROPAGATION_REQUIRES_NEW:该方法必须运行在自己的事务中。它将启动一个新的事务。如果一个现有的事务正在运行,将在这个方法的运行期间挂起。 
         PROPAGATION_SUPPORTS:当前方法不需要事务处理环境,但如果一个事务已经在运行的话,这个方法也可以在这个事务里运行。

       隔离级别配置型是可选的,默认为ISOLATION_DEFAULT,表示使用数据库默认的隔离级别。隔离级别其他选项的设置值如下:
       1、ISOLATION_READ_UNCOMMITTED;  ----- 未提交读,这个级别会出现脏读,不可重复读和幻读。脏读的设计知识为了提供非阻塞读,即查询不会受到任何增删改查的影响。
       2、ISOLATION_READ_COMMITTED;    --------提交读,这个级别会出现不可重复读和幻读。(Orancle默认隔离级别)。
       3、ISOLATION_REPEATABLE_READ;   ----------重复读,会出现幻读。
       4、ISOLATION_SERIALIZABLE;            ----------串行化;隔离级别最高,不允许出现脏读,不可重复读和幻读。即一个事务执行结束了另一个
事务才能执行。并发性也就最差。
       5、ISOLATION_DEFAULT:使用数据库默认的隔离级别。(Spring注解声明式事务默认的隔离级别)

      ( 基础知识回顾:
       脏读:一个事务可以读取另一个事务未提交的数据;
       不可重复读:在一个事务中不同时间段查询出不同的结果,可能被更新可能被删除;
       幻读:在一个事务中不同时间段查询,记录数不同。与不可重复读的区别是:在幻读中,已经读取的数据不会改变,只是与之前相比,会有更多的数据满足查询条件。(至于如何出现的上述情况,请自查)。)
       
       如果希望将匹配的方法设置为只读事务,可添加readOnly配置项,如上上图 -4 所示。

       因为Spring 默认的事务回滚规则为:运行期异常回滚,检查型异常不回滚。


基于tx/aop 命名空间的配置

      使用TransactionProxyFactoryBean 代理工厂类为业务类添加事务性支持,在学习了动态代理的知识后,理解起来应该比较直观。但它有以下明显的缺点:
       1、需要对每个需要事务支持的业务类进行单独的配置;
       2、在制定事务方法时,只能通过方法名进行定义,无法利用方法签名的其他信息进行定位(如方法入参,访问名称修饰符等);
       3、事务属性的配置串的规则比较麻烦,规则串虽然包括多项信息,但统一由逗号分隔的字符串来描述,不能利用IDE中的诱导输入功能,容易出错。
       4、在为业务类Bean 添加事务支持时,在容器中既需要定义业务类Bean(通常命名为 xxxTarget),又需要通过TransactionProxyFactoryBean对其进行代理以生成支持事务的代理Bean。
 
       这一切都是因为在低版本的Spring 中,没有引入强大的AOP切面描述语言而造成的。 Spring 在2.0 之后的重要改进就是引入了AspectJ 切面定义语言,这就使事务方法切面描述的难题迎刃而解了。

       Spring 在基于Schema 的配置中,添加了一个tx 命名空间,在配置文件中以明确结构化的方式定义事务属性,大大提高了配置事务属性的便利性。配合aop 命名空间所提供的切面定义这把利剑,业务类方法事务配置得到了大大的简化,而在描述能力上却得到了很大的提升。

       下图中可以看到通过tx和aop 命名空间基于 FactoryBean的事务配置方式进行替换。
     
       首先,需要在配置文件中引入tx 命名空间的声明,如<beans>元素中所示。采用aop/tx 定义事务方法时,它站在“局外人”的角度对IOC 容器中的Bean进行事务管理配置定义,再由Spring将这些配置织入对应的Bean中。

       在这一过程中我们看到了3种角色:通过aop/tx 定义的声明式事务配置信息、业务Bean 、Spring 容器。Spring 容器自动回将配置信息应用于 业务Bean,从容器中返回的业务Bean 已经是被织入事务增强的代理Bean,这样配置信息和业务Bean 在配置上不直接发生关系。
    
       相比较之下,在使用TransactionProxyFactoryBean进行事务配置时,TransactionProxyFactoryBean需要直接通过target 属性引用目标业务Bean,结果造成目标业务Bean往往需要使用target 进行命名,以避免和最终代理Bean 名称冲突。使用aop/tx 方式后,业务Bean 的名称不需要做任何“配合性”的调整,aop直接通过切点表达式语言就可以对Bean进行定位。从这个意义上来说,aop/tx 配置方式对业务Bean 是“无侵入”的,而TransactionProxyFactoryBean 的配置是“侵入式”的。

        在aop 命名空间中,通过切点表达式语言,将com.smart.service包下所有以Service 为后缀的类纳入需要进行事务增强的范围。配合<tx:advice> 的 <aop:advisor> 完成事务切面的定义。如上图中代码。

        <aop:advisor>引入的txAdvice 增强是在 tx 命名空间上定义的。首先,事务增强一定需要一个事务管理器的支持,<tx:advice>通过transaction-manager 属性引用了上图中②中的事务管理器(第14行代码)。
         下图是<tx:method> 元素属性表:
         
      
     
       在下一篇中探究注解式事务配置。

0 0
原创粉丝点击