`

设计模式之事务处理

阅读更多

转自 http://www.blogjava.net/killme2008/archive/2007/02/06/98217.html

 

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

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

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库。
    这篇短文只是介绍下我对事务上下文模式以及声明式事务管理实现基本原理的理解,如有错误,请不吝赐教。我的email:killme2008@gmail.com
分享到:
评论

相关推荐

    JAVA设计模式之事务处理

    JAVA设计模式之事务处理

    JAVA设计模式之事务处理[收集].pdf

    JAVA设计模式之事务处理[收集].pdf

    JAVA设计模式之事务处理.pdf

    事务处理是企业应用需要解决的最主要的问题之一。J2EE通过JTA提供了完整的事务管理能力,包括多个事务性资 源的管理能力。但是大部分应用都是运行在单一的事务性资源之上(一个数据库),他们并不需要全局性的事务...

    JAVA设计模式之事务处理.docx

    java事务设计模式

    关于系统构建中事务处理的思路分享!欢迎大家多多下载。

    【设计模式】之责任链模式1

    【设计模式】之责任链模式责任链模式概念责任链,顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果

    dao设计模式DAO 设计模式之事务界定疯.pdf

    DAO 不负责处理事务、会话或连接,而把这交给一个工具类,封装所有关于数据库的操作。 把Session的获取,语句的关闭等放在这个类更好。通常的设计把数据 库的代码放到DAO的实现类中,这样如 果某个DAO实现类设计不良...

    Java企业设计模式

    本书系统介绍了设计模式和体系结构模式,这些模式可应用于分布式和企业应用程序。其中主要包含与事务处理、分布式计算、并发性和时间等相关的模式,还介绍了在面向对象程序设计中的数据库模式。本书主要内容还包括...

    java 常用设计模式chm

    理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的...

    移动数据库中移动事务处理的关键技术研究

    计算技术和无线通讯技术的发展与结合使得一种全新的计算模式—移动... 本课题的主要研究目标是设计一个移动事务模型和事务处理协议来支持移动事务的移动性、频繁断接性和数据一致性。本人考虑了移动计算环境对移动...

    # 动态代理设计模式.md

    这种设计模式可以用于横切关注点的集中处理,如日志记录、事务管理、性能监控等。 目的 动态代理的目的是在不修改原始对象的情况下,对其方法调用进行控制和增强。通过代理对象,我们可以在方法调用前后插入附加...

    分布式事务实践 解决数据一致性

    第1章 课程介绍 介绍该课程的内容、学习成果、实例,...本章介绍TCC模式,也对微服务系统的几种设计模式,以及这些模式下分布式事务的实现模式进行了介绍。 9-1 TCC模式介绍 9-2 微服务架构的设计模式 第10章 课程总

    领域驱动设计与模式实践.pdf

    本文旨在从实践的角度探讨领域建模和设计,涉及如何着手处理领域模型并实际地实现它。我们将着眼于技术主管和架构师在实现过程中能用到的指导方针、最佳实践、框架及工具。领域驱动设计和开发也受一些架构、设计、...

    设计模式系列之命令模式

    但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。...

    二十三种设计模式【PDF版】

    之道 》,其中很多观点我看了很受启发,以前我也将"设计模式" 看成一个简单的解决方案,没有从一种高度来看待"设计模式"在软 件中地位,下面是我自己的一些想法: 建筑和软件某些地方是可以来比喻的 特别是中国传统建筑...

    基于SOA模式的事务定时提醒系统全套

    事务处理定时提醒系统 数据库+论文+源代码 毕业设计

    JS设计模式之命令模式概念与用法分析

    本文实例讲述了JS设计模式之命令模式概念与用法。分享给大家供大家参考,具体如下: 客户创建命令;调用者执行该命令;接收者在命令执行时执行相应操作 简单命令对象一般用来消除二个对象(调用者和接收者)之间的...

    领域驱动设计与模式实战

    2.4 针对具体应用程序类型的设计模式 2.5 领域模式 2.6 小结 第3章 TDD与重构 3.1 TDD 3.1.1 TDD流程 3.1.2 演示 3.1.3 设计效果 3.1.4 问题 3.1.5 下一个阶段 3.2 模拟和桩 3.2.1 典型单元测试 3.2.2 声明独立性 ...

    图书管理系统,使用MVC设计模式(vo,dao,service,servlet),前端采用Bootstrap

    简单的图书管理系统,前端采用了Bootstrap,后端采用MVC的设计模式,包括:vo,dao,service,servlet,filter,listener;整个项目实现了图书的增删改查,出入库操作,管理员的增删改查操作,操作日志记录,登录...

Global site tag (gtag.js) - Google Analytics