`
rq2_79
  • 浏览: 239773 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

JAVA设计模式之事务处理

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

java 代码
  1. public interface BookStoreManager{    
  2.           public boolean buyBook(String bookId,int quantity)throws SystemException;    
  3.           ....其他业务方法    
  4. }   

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


java 代码
  1. public class BookStoreManagerImpl implements BookStoreManager{    
  2.          public boolean buyBook(String bookId)throws SystemException{    
  3.               Connection conn=ConnectionManager.getConnection();//获取数据库连接    
  4.               boolean b=false;    
  5.                  
  6.               try{    
  7.                   conn.setAutoCommit(false);  //取消自动提交    
  8.                   BookDAO bookDAO=DAOFactory.getBookDAO();    
  9.                   CustomerDAO customerDAO=DAOFactory.getCustomerDAO();    
  10.                     //尝试从库存中取书    
  11.                   if(BookDAO.reduceInventory(conn,bookId,quantity)){    
  12.                        BigDecimal price=BookDAO.getPrice(bookId);  //取价格    
  13.                        //从客户帐户中扣除price*quantity的费用    
  14.                        b=    
  15.                        CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity));    
  16.                        ....    
  17.                        其他业务方法,如通知管理员,生成定单等.    
  18.                         ...    
  19.                        conn.commit();   //提交事务    
  20.                        conn.setAutoCommit(true);    
  21.                   }    
  22.                }catch(SQLException e){    
  23.                   conn.rollback();   //出现异常,回滚事务    
  24.                   con.setAutoCommit(true);    
  25.                   e.printStackTrace();    
  26.                   throws new SystemException(e);      
  27.                }    
  28.                return b;    
  29.        }    
  30. }   

    然后是业务代表工厂:  

java 代码
  1. public final class ManagerFactory {    
  2.     public static BookStoreManager getBookStoreManager() {    
  3.        return new BookStoreManagerImpl();    
  4.     }    
  5.  }   

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

        我们有更为优雅的设计来解决这类问题,如果我们把Transaction Context的控制交给一个被业务代表对象、DAO和其他Component所共知的外部对象。当业务代表对象的某个方法需要事务管理时,它提示此外部对象它希望开始一个事务,外部对象获取一个连接并且开始数据库事务。也就是将事务控制从service层抽离,当web层调用service层的某个业务代表对象时,返回的是一个经过Transaction Context外部对象包装(或者说代理)的业务对象。此代理对象将请求发送给原始业务代表对象,但是对其中的业务方法进行事务控制。那么,我们如何实现此效果呢?答案是JDK1.3引进的动态代理技术。动态代理技术只能代理接口,这也是为什么我们需要业务接口BookStoreManager的原因。
    首先,我们引入这个Transaction Context外部对象,它的代码其实很简单,如果不了解动态代理技术的请先阅读其他资料。 
java 代码
  1. import java.lang.reflect.InvocationHandler;    
  2. import java.lang.reflect.Method;    
  3. import java.lang.reflect.Proxy;    
  4.   
  5. import java.sql.Connection;    
  6.   
  7. import com.strutslet.demo.service.SystemException;    
  8.   
  9. public final class TransactionWrapper {    
  10.   
  11.     /**   
  12.      * 装饰原始的业务代表对象,返回一个与业务代表对象有相同接口的代理对象   
  13.      */    
  14.     public static Object decorate(Object delegate) {    
  15.         return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),    
  16.                 delegate.getClass().getInterfaces(), new XAWrapperHandler(    
  17.                         delegate));    
  18.     }    
  19.        
  20.     //动态代理技术    
  21.     static final class XAWrapperHandler implements InvocationHandler {    
  22.         private final Object delegate;    
  23.   
  24.         XAWrapperHandler(Object delegate) {    
  25.            this.delegate = delegate;    
  26.         }    
  27.            
  28.         //简单起见,包装业务代表对象所有的业务方法    
  29.         public Object invoke(Object proxy, Method method, Object[] args)    
  30.                 throws Throwable {    
  31.             Object result = null;    
  32.             Connection con = ConnectionManager.getConnection();    
  33.             try {    
  34.                 //开始一个事务    
  35.                 con.setAutoCommit(false);    
  36.                 //调用原始业务对象的业务方法    
  37.                 result = method.invoke(delegate, args);    
  38.                 con.commit();   //提交事务    
  39.                 con.setAutoCommit(true);    
  40.             } catch (Throwable t) {    
  41.                 //回滚    
  42.                 con.rollback();    
  43.                 con.setAutoCommit(true);    
  44.                 throw new SystemException(t);    
  45.             }    
  46.   
  47.             return result;    
  48.         }    
  49.     }    
  50. }    


        正如我们所见,此对象只不过把业务对象需要事务控制的业务方法中的事务控制部分抽取出来而已。请注意,业务代表对象内部调用自身的方法将不会开始新的事务,因为这些调用不会传给代理对象。如此,我们去除了代表重复的味道。此时,我们的业务代表对象修改成:
java 代码
  1. public class BookStoreManagerImpl implements BookStoreManager {    
  2.     public boolean buyBook(String bookId)throws SystemException{    
  3.           Connection conn=ConnectionManager.getConnection();// 获取数据库连接    
  4.           boolean b=false;    
  5.           try{    
  6.               BookDAO bookDAO=DAOFactory.getBookDAO();    
  7.               CustomerDAO customerDAO=DAOFactory.getCustomerDAO();    
  8.               // 尝试从库存中取书    
  9.               if(BookDAO.reduceInventory(conn,bookId,quantity)){    
  10.                   BigDecimal price=BookDAO.getPrice(bookId);  // 取价格    
  11.                   // 从客户帐户中扣除price*quantity的费用    
  12.                   b=    
  13.                   CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity));    
  14.                   ....    
  15.                   其他业务方法,如通知管理员,生成定单等.    
  16.                   ...    
  17.               }    
  18.           }catch(SQLException e){    
  19.              throws new SystemException(e);    
  20.           }    
  21.           return b;    
  22.     }    
  23.     ....    
  24. }    


        可以看到,此时的业务代表对象专注于实现业务逻辑,它不再关心事务控制细节,把它们全部委托给了外部对象。业务代表工厂也修改一下,让它返回两种类型的业务代表对象:
java 代码
  1. public final class ManagerFactory {    
  2.       //返回一个被包装的对象,有事务控制能力    
  3.       public static BookStoreManager getBookStoreManagerTrans() {    
  4.           return (BookStoreManager) TransactionWrapper    
  5.                   .decorate(new BookStoreManagerImpl());    
  6.       }    
  7.       //原始版本    
  8.       public static BookStoreManager getBookStoreManager() {    
  9.          return new BookStoreManagerImpl();    
  10.       }    
  11.       ......    
  12.    }    


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

相关推荐

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

    ### JAVA设计模式之事务处理 #### 一、引言 在企业级应用开发中,事务处理是一项关键的技术,尤其在涉及多个数据操作时尤为重要。Java技术体系中的J2EE(Java 2 Enterprise Edition)提供了强大的事务管理机制——...

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

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

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

    Java设计模式中的事务处理是构建企业级应用时不可或缺的一部分,主要关注数据的一致性和完整性。在J2EE环境中,Java Transaction API (JTA) 提供了全面的事务管理能力,适用于涉及多个事务资源的情况。然而,对于...

    Java事务设计模式_java_事务设计模式_

    以上内容主要围绕Java中的事务处理策略展开,包括事务的基本概念、不同的事务管理模型以及事务设计模式的应用。了解和掌握这些知识,对于开发高可靠性的Java应用至关重要。在实际项目中,应根据系统需求和复杂度选择...

    java事务 - 模板设计模式

    总结来说,Java事务模板设计模式结合ThreadLocal,提供了一种高效、健壮的事务管理策略。它减少了代码的重复性,提高了代码的可读性和可维护性,同时通过ThreadLocal解决了并发环境下的事务隔离问题。理解并熟练应用...

    java.设计模式大作业 薪资交付系统

    在Java编程领域,设计模式是一种解决常见问题的模板或最佳实践,它被广泛应用于构建可扩展、可维护的软件系统。在这个"薪资交付系统"的大作业中,我们可以预期会涉及多种设计模式,这些模式将帮助我们高效地组织代码...

    DAO设计模式 DAO 设计模式 JAVA设计模式

    DAO(Data Access Object)设计模式是软件开发中一种常见的用于处理数据访问的模式,它将业务逻辑与数据访问逻辑分离,使得代码结构更加清晰,更易于维护和测试。在Java开发中,DAO模式广泛应用于数据库操作,为应用...

    Java 企业设计模式

    Java企业设计模式是软件开发领域中的重要组成部分,它是一套经过实践验证的解决方案,用于解决在Java环境中构建大型、复杂企业应用时常见的问题。这些模式是经验丰富的开发者们通过不断尝试和总结,形成的最佳实践,...

    javaDAO设计模式

    Java DAO(Data Access Object)设计模式是一种在软件工程中用于数据库操作的常见设计模式,它主要目的是将业务逻辑层与数据访问层进行解耦,提高代码的可复用性和可测试性。DAO模式的核心思想是创建一个接口或者...

    设计模式,分布式设计模式,Java设计模式

    在Java这样的面向对象编程语言中,设计模式的应用尤为重要,它们能够帮助开发者编写出更加灵活、可扩展和易于维护的代码。 1. **单例模式**:确保一个类只有一个实例,并提供全局访问点。在Java中,单例模式通常...

    Java Web设计模式之道 [蒋海昌编著] 高清扫描带书签版本

    《Java Web设计模式之道》是蒋海昌编著的一本专著,主要针对Java Web开发中的设计模式进行了深入探讨。这本书以高清扫描的形式提供,并带有书签,方便读者快速定位和查阅。书中详细阐述了如何在Java Web应用程序中...

    设计模式课程设计---使用5个以上不同的设计模式完成(java)

    在本设计模式课程设计中,我们重点探讨了五个核心的设计模式:原型模式、单例模式、抽象工厂模式、代理模式和建造者模式。这些模式在Java编程中具有广泛的应用,能够帮助开发者创建更加灵活、可扩展和易于维护的代码...

    JAVA设计模式(代理模式)

    **Java设计模式——代理模式详解** 代理模式是软件设计模式中的一个重要组成部分,它在Java编程中扮演着举足轻重的角色。代理模式的核心思想是为一个对象提供一个替身,这个替身即代理对象,代理对象可以控制对原...

    Java企业设计模式.rar

    Java企业设计模式是软件开发中的一种重要思想,它在Java应用程序开发中扮演着核心角色,尤其是在大型企业级项目中。设计模式是对在特定上下文中反复出现的问题及其解决方案的描述,这些解决方案已被证明在许多情况下...

    java事务设计策略

    本文详细介绍了Java事务设计策略中的关键技术点,包括事务的基本概念、Java事务管理机制、设计模式以及高级技巧。通过对这些知识点的理解和应用,开发者能够在构建Java应用时更加高效地管理事务,确保数据的一致性和...

    经典设计模式讲解以及项目实战(Java版)

    "经典设计模式讲解以及项目实战(Java版)"是一份专为Java开发者准备的学习资源,旨在帮助他们理解和应用这些模式到实际项目中。设计模式提供了一种标准化的方法来处理软件设计中的重用性、灵活性和可维护性,使代码...

Global site tag (gtag.js) - Google Analytics