`
gao_20022002
  • 浏览: 160644 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring之事务管理理解

阅读更多

转载:学习之用。

 

事务处理是企业应用需要解决的最主要的问题之一。J2EE通过JTA提供了完整的事务管理能力,包括多个事务性资源的管理能力。但是大部分应用都是运行在单一的事务性资源之上(一个数据库),他们并不需要全局性的事务服务。本地事务服务已然足够(比如JDBC事务管理)。
    本文并不讨论应该采用何种事务处理方式,主要目的是讨论如何更为优雅地设计事务服务。仅以JDBC事务处理为例。涉及到的DAO,Factory,Proxy,Decorator等模式概念,请阅读相关资料。
    也许你听说过,事务处理应该做在service层,也许你也正这样做,但是否知道为什么这样做?为什么不放在DAO层做事务处理。显而易见的原因是业务层 接口的每一个方法有时候都是一个业务用例(User Case),它需要调用不同的DAO对象来完成一个业务方法。比如简单地以网上书店购书最后的确定定单为例,业务方法首先是调用BookDAO对象(一般 是通过DAO工厂产生),BookDAO判断是否还有库存余量,取得该书的价格信息等,然后调用CustomerDAO从帐户扣除相应的费用以及记录信 息,然后是其他服务(通知管理员等)。简化业务流程大概如此:
    注意,我们的例子忽略了连接的处理,只要保证同一个线程内取的是相同的连接即可(可用ThreadLocal实现):

    首先是业务接口,针对接口,而不是针对类编程:

public interface BookStoreManager{
          public boolean buyBook(String bookId,int quantity)throws SystemException;
          ....其他业务方法
    }

 

    接下来就是业务接口的实现类??业务对象:

public class BookStoreManagerImpl implements BookStoreManager{
         public boolean buyBook(String bookId)throws SystemException{
              Connection conn=ConnectionManager.getConnection();//获取数据库连接
              boolean b=false;
             
              try{
                  conn.setAutoCommit(false);  //取消自动提交
                  BookDAO bookDAO=DAOFactory.getBookDAO();
                  CustomerDAO customerDAO=DAOFactory.getCustomerDAO();
                    //尝试从库存中取书
                  if(BookDAO.reduceInventory(conn,bookId,quantity)){
                       BigDecimal price=BookDAO.getPrice(bookId);  //取价格
                       //从客户帐户中扣除price*quantity的费用
                       b=
                       CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity));
                       ....
                       其他业务方法,如通知管理员,生成定单等.
                        ...
                       conn.commit();   //提交事务
                       conn.setAutoCommit(true);
                  }
               }catch(SQLException e){
                  conn.rollback();   //出现异常,回滚事务
                  con.setAutoCommit(true);
                  e.printStackTrace();
                  throws new SystemException(e);  
               }
               return b;
         }
    } 

 

    然后是业务代表工厂:

public final class ManagerFactory {
      public static BookStoreManager getBookStoreManager() {
         return new BookStoreManagerImpl();
      }
   } 
 


    这样的设计非常适合于DAO中的简单活动,我们项目中的一个小系统也是采用这样的设计方案,但是它不适合于更大规模的应用。首先,你有没有闻到代码重复的 bad smell?每次都要设置AutoCommit为false,然后提交,出现异常回滚,包装异常抛到上层,写多了不烦才怪,那能不能消除呢?其次,业务代 表对象现在知道它内部事务管理的所有的细节,这与我们设计业务代表对象的初衷不符。对于业务代表对象来说,了解一个与事务有关的业务约束是相当恰当的,但 是让它负责来实现它们就不太恰当了。再次,你是否想过嵌套业务对象的场景?业务代表对象之间的互相调用,层层嵌套,此时你又如何处理呢?你要知道按我们现 在的方式,每个业务方法都处于各自独立的事务上下文当中(Transaction Context),互相调用形成了嵌套事务,此时你又该如何处理?也许办法就是重新写一遍,把不同的业务方法集中成一个巨无霸包装在一个事务上下文中。

    我们有更为优雅的设计来解决这类问题,如果我们把Transaction Context的控制交给一个被业务代表对象、DAO和其他Component所共知的外部对象。当业务代表对象的某个方法需要事务管理时,它提示此外部 对象它希望开始一个事务,外部对象获取一个连接并且开始数据库事务。也就是将事务控制从service层抽离,当web层调用service层的某个业务 代表对象时,返回的是一个经过Transaction Context外部对象包装(或者说代理)的业务对象。此代理对象将请求发送给原始业务代表对象,但是对其中的业务方法进行事务控制。那么,我们如何实现 此效果呢?答案是JDK1.3引进的动态代理技术。动态代理技术只能代理接口,这也是为什么我们需要业务接口BookStoreManager的原因。
    首先,我们引入这个Transaction Context外部对象,它的代码其实很简单,如果不了解动态代理技术的请先阅读其他资料。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import java.sql.Connection;

import com.strutslet.demo.service.SystemException;

public final class TransactionWrapper {

    /**
     * 装饰原始的业务代表对象,返回一个与业务代表对象有相同接口的代理对象
     */
    public static Object decorate(Object delegate) {
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(), new XAWrapperHandler(
                        delegate));
    }
   
    //动态代理技术
    static final class XAWrapperHandler implements InvocationHandler {
        private final Object delegate;

        XAWrapperHandler(Object delegate) {
           this.delegate = delegate;
        }
       
        //简单起见,包装业务代表对象所有的业务方法
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            Object result = null;
            Connection con = ConnectionManager.getConnection();
            try {
                //开始一个事务
                con.setAutoCommit(false);
                //调用原始业务对象的业务方法
                result = method.invoke(delegate, args);
                con.commit();   //提交事务
                con.setAutoCommit(true);
            } catch (Throwable t) {
                //回滚
                con.rollback();
                con.setAutoCommit(true);
                throw new SystemException(t);
            }

            return result;
        }
    }
} 

    正如我们所见,此对象只不过把业务对象需要事务控制的业务方法中的事务控制部分抽取出来而已。请注意,业务代表对象内部调用自身的方法将不会开始新的事务,因为这些调用不会传给代理对象。如此,我们去除了代表重复的味道。此时,我们的业务代表对象修改成:

public class BookStoreManagerImpl implements BookStoreManager {
    public boolean buyBook(String bookId)throws SystemException{
          Connection conn=ConnectionManager.getConnection();// 获取数据库连接
          boolean b=false;
          try{
              BookDAO bookDAO=DAOFactory.getBookDAO();
              CustomerDAO customerDAO=DAOFactory.getCustomerDAO();
              // 尝试从库存中取书
              if(BookDAO.reduceInventory(conn,bookId,quantity)){
                  BigDecimal price=BookDAO.getPrice(bookId);  // 取价格
                  // 从客户帐户中扣除price*quantity的费用
                  b=
                  CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity));
                  ....
                  其他业务方法,如通知管理员,生成定单等.
                  ...
              }
          }catch(SQLException e){
             throws new SystemException(e);
          }
          return b;
    }
    ....
}

    可以看到,此时的业务代表对象专注于实现业务逻辑,它不再关心事务控制细节,把它们全部委托给了外部对象。业务代表工厂也修改一下,让它返回两种类型的业务代表对象:

public final class ManagerFactory {
      //返回一个被包装的对象,有事务控制能力
      public static BookStoreManager getBookStoreManagerTrans() {
          return (BookStoreManager) TransactionWrapper
                  .decorate(new BookStoreManagerImpl());
      }
      //原始版本
      public static BookStoreManager getBookStoreManager() {
         return new BookStoreManagerImpl();
      }
      ......
   }

    我们在业务代表工厂上提供了两种不同的对象生成方法:一个用于创建被包装的对象,它会为每次方法调用创建一个新的事务;另外一个用于创建未被包装的版本,它用于加入到已有的事务(比如其他业务代表对象的业务方法),解决了嵌套业务代表对象的问题。
   我们的设计还不够优雅,比如我们默认所有的业务代表对象的方法调用都将被包装在一个Transaction Context。可事实是很多方法也许并不需要与数据库打交道,如果我们能配置哪些方法需要事务声明,哪些不需要事务管理就更完美了。解决办法也很简单, 一个XML配置文件来配置这些,调用时判断即可。说到这里,了解spring的大概都会意识到这不正是声明式事务控制吗?正是如此,事务控制就是AOP的 一种服务,spring的声明式事务管理是通过AOP实现的。AOP的实现方式包括:动态代理技术,字节码生成技术(如CGLIB库),java代码生成 (早期EJB采用),修改类装载器以及源代码级别的代码混合织入(aspectj)等。我们这里就是利用了动态代理技术,只能对接口代理;对类的动态代理 可以使用cglib库。

分享到:
评论
1 楼 itshu 2008-10-24  
[b][/b][i][/i][u][/u]
引用
[img][/img][url][/url][flash=200,200][/flash]

相关推荐

    spring 事务管理的理解

    spring 事务管理的理解

    深入理解spring的事务管理机制

    【免费】深入描述spring的事务处理机制,很不错的资源。

    spring事务管理几种方式代码实例

    spring事务管理几种方式代码实例:涉及编程式事务,声明式事务之拦截器代理方式、AOP切面通知方式、AspectJ注解方式,通过不同方式实例代码展现,总结spring事务管理的一般规律,从宏观上加深理解spring事务管理特性...

    深入理解Spring声明式事务:源码分析与应用实践

    此外,Spring事务管理器支持多种类型的事务策略,包括不同的传播行为和隔离级别,允许开发者根据具体业务场景选择最合适的事务管理策略。深入理解Spring声明式事务的工作原理,不仅能帮助开发者更高效地使用Spring...

    spring杂谈 作者zhang KaiTao

    1.9 Spring对事务管理的支持的发展历程(基础篇) 1.10 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。 1.11 在spring中获取代理对象代理的目标对象工具类 1.12 如何为...

    MyBatis 事务管理解析:颠覆你⼼中对事务的理解.pdf

    事务本⾝并不存在什么传播特性,不要混淆事务本⾝和Spring的事务应⽤策略。(当然,找⼯作⾯试时,还是可以巧妙的描述传播 特性的) 2.⼀说到事务,⼈们可能⼜会想起create、begin、commit、rollback、close、...

    Spring攻略PDF版

     第8章 Spring中的事务管理   第9章 Spring对ORM的支持   第10章 Spring MVC框架   第11章 整合Spring与其他Web框架   第12章 Spring对测试的支持  第三部分 高级主题  第13章 Spring ...

    Spring攻略中文版PDF

     第8章 Spring中的事务管理   第9章 Spring对ORM的支持   第10章 Spring MVC框架   第11章 整合Spring与其他Web框架   第12章 Spring对测试的支持  第三部分 高级主题  第13章 Spring ...

    深入解析Spring核心API: 打造高效Java应用

    本文深入探讨了Spring的核心API,包括BeanFactory、BeanDefinition以及事务管理等关键组件。 BeanFactory是Spring框架的心脏,作为Bean容器的根接口,它采用工厂模式来管理Bean的生命周期。BeanDefinition则详细描述...

    Spring与Hibernate整合事务管理的理解

    主要介绍了Spring与Hibernate整合事务管理的理解的相关资料,需要的朋友可以参考下

    spring in action英文版

     5.1.2 理解Spring对事务管理的支持  5.1.3 介绍Spring的事务管理器  5.2 在Spring中编写事务  5.3 声明式事务  5.3.1 理解事务属性  5.3.2 声明一个简单的事务策略  5.4 通过方法名声明事务 ...

    Spring攻略英文版(附带源码)

     第8章 Spring中的事务管理   第9章 Spring对ORM的支持   第10章 Spring MVC框架   第11章 整合Spring与其他Web框架   第12章 Spring对测试的支持  第三部分 高级主题  第13章 Spring Security...

    Spring 2.0 开发参考手册

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. <tx:advice/> 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 插入事务操作 9.5.8. ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. <tx:advice/> 有关的设置 9.5.6. 使用 @Transactional 9.5.6.1. @Transactional 有关的设置 ...

    Spring3.X编程技术与应用,完整扫描版

    Spring基础概念与工具、用SpringJdbcTemplate访问数据库、使用Mayen工程、Spring MVC编程、基于 MVC的资源共享网站设计、Spring的AOP编程、Spring的安全访问控制、Spring的事务管理、 Spring的任务执行与调度、...

    基于SSH(Struts2、Spring与Hibernate)框架的学生成绩管理系统.rar

    基于SSH(Struts2、Spring与Hibernate)框架的学生成绩管理系统 (1) 整合Struts2、Spring和...+ 掌握Spring的开发步骤,理解依赖注入、AOP、事务管理等 + 掌握Struts2、Spring和Hibernate框架的整合 + 掌握分页技术

    spring.net中文手册在线版

    14.5.1.理解Spring.NET声明式事务管理的实现 14.5.2.第一个例子 14.5.3.Transaction特性的设置 14.5.4.通过AutoProxyCreator使用声明式事务 14.5.5.通过TransactionProxyFactoryObject使用声明式事务 14.5.6. 通过...

    《精通Spring2.X企业应用开发详解》随书源码1-15章

    Spring容器高级主题 第6章 Spring AOP基础 第7章 基于@AspectJ和Schema的 第7章 AOP 第3篇 数据库访问 第8章 Spring对DAO的支持 第9章 Spring的事务管理 第10章 使用Spring JDBC访问数据库 ...

    spring面试题大全

    spring面试题大全,希望可以帮助到大家。 * Spring的优点有什么? * 描述一下spring中实现DI(dependency injection)的几... 请介绍下spring的事务管理 如何在spring的applicationContext.xml使用JNDI而不是DataSource

    spring chm文档

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. <tx:advice/> 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 插入事务操作 9.5.8. ...

Global site tag (gtag.js) - Google Analytics