业务逻辑和数据库访问决策 这里有2种完全不同的方法来设计JAVA企业程序,其中一种选择是采用标准EJB2实现途径(approach)。我更愿意称这种方法为重量级实现途径,当你使用重量级实现途径时你需要用会话beans(session bean)和消息驱动 beans(message-driven bean)去实现业务逻辑。你也可以使用DAOs(data access object)或者实体bean去访问业务逻辑 另外一种选择是使用POJOs 和轻量级构架,这种方式我称为POJO实现途径。当使用POJOs实现途径时,你的业务逻辑完全由POJO来实现。你可以使用持久型构架又叫做对象/关系映射构架(a.k.a=also know as )例如Hibernate 或者 JDO来访问数据库,再用Spring AOP(面向层面编程)来提供企业服务,比如事务管理和安全。 EJB3由于融合了POJOs和其他一些轻量级概念,所以对两者(指轻量级和重考锻揪叮┑那植皇呛芮宄>俑隼樱琍OJO中的实体bean既可以再EJB容器内运行,也可以再EJB容器外运行,然而POJOs中的会话bean和消息驱动bean仍然有重量级的行为,因为他们只能在EJB容器内部运行。所以,显而易见的,EJB3既是重量级的又有POJO的特性。EJB3中的实体bean是轻量级实现途径中的一部分。 在开发过程中,首要的是从各种各样的设计中选择到底采用重量级实现途径还是采用POJO实现途径。决策可以影响程序的几个方面,包括业务逻辑结构和数据访问机制。为了帮助从两种实现途径中择其一,来看这张典型的企业应用程序结构图,结构图在图示1中,而且在设计过程中就必须判断到底使用那种策略。 Figure 1. A typical application architecture and the key business logic and database access design decisions. 程序由网络基本表示层、业务层、持久层组成。网络基本表示层负责HTTP请求和为一般的浏览器客户端、XML和其他的胖体客户端生成HTML,比如为Ajax基本客户端生成HTML.业务层被表示层调用,用来实现程序业务逻辑。持久层被业务逻辑层用来访问外部数据源,比如数据库和其他程序。 表示层的设计不在本篇文章讨论之内,来看图表的其他部分,我们需要决定业务层结构的接口,这个接口是提供给表示层以及其他客户端的。而且还需要决定怎样访问能供多个程序访问的数据库。我们还必须决定如何处理短期事务处理事务和长期事务处理事务的并发问题。这些加起来一共有5种决策。每种决策都是要设计者来制定,为了能看懂演示图(big picture)要求每个开发者也都了解这些策略。 这些决策直接决定程序业务和表示层设计的特点。当然,还要决定一些其他很重要的决策。比如业务处理(transactions)、安全问题、缓存问题以及如何整合程序,但是关于这些问题通常在其他文献中讨论在图表1中显示的五种决策,每种决策都有多种选择。每种选择根据它要解决的实际问题都有相应的优缺点。后续章节中,你会发现每种决策针对一个或多个领域时,在功能性、易开发性、可维护性和可用性方面有不同的平衡点。尽管我是POJO实现途径的超级大FANS,但是仍然需要了解其优缺点,以便于为你的程序做最好的选择下面我们来了解一下每种决策的大纲和其选项。 决策1:组织业务逻辑 现在,很多的注意力都集中在某项技术的优点和缺点,尽管这很重要,但是在本质上你需要了解如何建构你的业务逻辑。如果不考虑如何组织就去写代码是非常简单的。例如,为一个会话BEAN添加代码要比在域模式(domain model.: An object model of the domain that incorporates both behavior and data.)中判断应该添加那种新特性要简单的多。理论上你仍然需要刻意的为你的软件设计最合适的业务逻辑。毕竟我相信你有过修改别人垃圾结构代码的惨痛经验 关键的决策是:到底应该用面向对象的实现途径还是面向过程的实现途径来实现你的程序。这个不是关于技术的决策,但是你技术上的决策可以潜在的约束你的业务逻辑的组织结构。采用EJB2技术,有利于面向过程设计,然而POJOs和轻量级构架可以让你为特殊的程序选择最好的实现途径 采用过程式设计 采种这种实现途径的一个很重要的特点是,用于实现某种行为的类和数据存储区是分开的。在EJB2的应用程序中,这种方式的业务逻辑和图表2中的设计是非常相似的。这种设计的核心全都集中在EJB或者POJO的行为上,因为他们实现了事务脚本,并且还操作那些 “哑”对象数据(因为他们只拥有很少的行为,大部分都是数据)。因为大部分的行为都集中在少量的大型类上,所以代码会变的很难理解与维护。 Figure 2. The structure of a procedural design: large transaction script classes and many small data objects 这种设计具有高面向过程的特性,而且基本不依靠面向对象语言的特性。如果你曾经使用过C或者其他非面向对象语言的话,你应该用过这种设计模式。如果这种模式很适合你的设计的话,用这种模式设计也是一种不错的选择。 这种直观的过程式开发途径,非常的诱人,因为你只需要写代码就好了,不用考虑如何组织你的类文件。但问题是,如果你的业务逻辑非常的复杂,那么你的代码会变的噩梦般的难以维护。所以,除非你要写的程序非常的简单,否则你应该用面向对象设计你的程序,而不要受面向过程的代码的诱惑。 采用面向对象设计 Figure 3. The structure of a domain model: small classes that have state and behavior 面向对象设计有许多的好处,包括可以提高可维护性和可延展性。你可以用EJB2的实体bean来实现一个简单的对象模型。但是如果像要获得更多的好处的话,必须要使用POJOs技术和轻量级持久层构架——比如Hibernate和JDO技术。POJO可以让你开发丰富的模型,这些模型可以拥有继承和回调等特点。而轻量级持久层构架可以让你很简单的从对象模型映射到数据库。 对象模型的另外一个名字是域模型,Fowler称这种由面向对象途径来开发的业务逻辑叫做域模型设计模式。(就是类的设计是直接用来解决问题的,则这种设计模式叫做域模型设计模式) 表模型设计模式 决策2:封装业务逻辑 前面几章,我没有提及如何组织业务逻辑。你必须决定业务逻辑有什么样的接口。业务逻辑的接口由一些数据和方法组成,这些数据和方法由表示层来调用。在设计接口时重点需要考虑的是:应该封装哪些业务逻辑的操作,而哪些操作不应该显示给表示层。封装接口可以提高程序的可维护性,因为通过隐藏业务逻辑的操作细节,可以实现修改业务逻辑而不影响表示层。缺点是,你必须为封装业务逻辑而特意的写很多的代码。 你还需要考虑其他重要的问题,比如如何处理事务处理,安全,和远程调用问题。通常这些也是业务逻辑接口要负责的问题。为了保证数据的连贯性,业务层的接口必须保证每个事务处理中的调用都能执行。同样,也要验证调用者是否有权限调用业务方法。业务层接口还要负责处理一些远程客户端的问题。 来考虑一下选项。 EJB session faç;ade Figure 4. Encapsulating the business logic with an EJB session faç;ade 在这种设计模式中,表示层也许是通过远程来调用facade(相当于session的一个高级接口),EJB容器从facade中得到这个调用,并验证调用者的权限,然后开始一个业务处理。这个时候facade调用底层的业务对象,而这些业务对象负责实现具体的业务逻辑。等Facade返回后,EJB容器提交业务处理或者让该业务处理循环等待。 不幸的是,使用EJB session facade有一些严重的缺点。比如,EJB的会话bean只能在EJB容器中运行,这样就托慢了开发和测试周期。另外,如果用EJB2,则用来向表示层传输数据的数据传输对象的开发和维护就会变的很枯燥而且旷日持久。 POJO facade Figure 5. Encapsulating the business logic with a POJO faç;ade 表示层调用POJO facade, POJO facade 调用业务对象。和EJB容器截获EJB facade方式一样,AOP通过“拦截机”来截获POJO facade,并验证调用者的权限,然后开始提交业务处理或让该业务循环等待。 通过在应用程序服务器外部开发和调试业务逻辑,对POJO facade的开发可以变的很简单,同时还可以获得许多EJB中会话Bean的好处,比如声明事务处理和安全。关键是,你可以少写点代码。你可以避免写数据传输对象类,因为POJO facade可以将对象域直接反馈给表示层;你可以使用依赖注射的方式来将应用程序组装起来,而不用在为JNDI写查找代码了。 然而,有些时候不能那用POJO facade,比如它不能参与到远程客户端建立的分布式事务处理。 暴露模型域模式 Figure 6. Using an exposed domain model 在图表6的设计中,表示层不通过facade而直接调用域对象,Spring AOP仍然提供服务,例如事务处理管理和安全。 用这种实现途径的一个重要的好处是,业务层不需要知道哪些对象需要调用,也不用知道那些需要返回给表示层。尽管这挺起来很简单,但是你会发现一些缺点。这会增加表示层的复杂度,因为你必须处理对数据库的连接。而且在基于Web的应用程序中,事务处理管理也要非常小心,因为在表示层将数据反馈给浏览器之前,事务处理的数据必须保持正确。 决策3:访问数据库 无论你怎样对业务逻辑怎样的组织和封装,最终你还是要从数据库中取数据出来。在经典的J2EE应用程序中,你有2个选择:JDBC——这个需要很多的底层代码;或者实体Bean——这个用起来非常困难,而且缺少重要特征。相比来说,使用轻量级构架令人高兴的事情之一就是:你有一些新的而且更有力的方法去访问数据库,而且这种方法可以显著的减少访问数据库的代码。让咱们来进一步研究 直接用JDBC会有什么问题 如果你的程序必须直接运行SQL语句的话,那前面两个问题是无法避免的。有时候为了获得好的性能,必须要全力的写SQL语句,包括供应商提供的那些特殊东西。由于许多业务上的原因,持久层可能会产生混乱的SQL语句,为了防止这种情况,DBA可能要求你的程序来完全控制SQL语句的执行。通常,团队买进的关系型数据库过于庞大,以至于应用程序工作时会出现一些和数据库有关的琐碎事务。根据“iBATIS in Action”的作者说这里会有一种情况出现:“数据库或者SQL语句本身存在的时间比程序代码存在的时间还要长,或者同一段SQL语句或数据库有多个程序的版本。有些情况下,程序已经用另外一种语言重写了,但是SQL语句和数据库却没有太大的改变。” 如果直接使用SQL弄的你筋疲力尽,那么很幸运,这里有一种直接执行SQL语句的构架,它可比用JDBC要容易多了。当然了,这就是iBATIS. 使用iBATIS iBATIS 不仅将应用程序完全的与“数据库连接”、具体的SQL语句隔绝开来,更实现了通过XML描述文档来将JavaBean 映射到SQL语句。它用Java bean 内省机制来将“道具bean(bean properties)”映射为相应的数据库语句占位符,而且它可以将ResultSet后的结果构造为bean.它还可以通过数据库生成主键,自动加载相关的对象、实现缓存和lazy loading.这样,iBATIS 就除去了许多执行SQL语句带来的苦差。通过编辑XML描述文档和调用少量的iBATIS的API,代替了写大量的JDBC底层代码。 使用持久层框架 在持久层构架上EJB也有它的短处:实体bean.EJB2的实体bean有很多的不足,而且开发和测试它会变得非常的枯燥。最后,很少用EJB2的实体bean了。在EJB3中会说明那些问题。 两种最有流行的轻量级持久层构架是JDO和Hibernate,前者是Sun的标准框架,后者是开源工程。两种框架都可以为POJO类提供持久层事务处理。你可以用POJO类来开发和测试你的业务逻辑,而不用担心持久层的问题,这个时候它会将类映射到数据库中的schema.另外,他们两个都可以在服务器程序外部或者内部,这样可以进一步降低开发难度。用Hibernate和JDO来进行开发比用老的EJB2的实体bean要舒服的多。 除了要决定怎样访问数据库外,还要决定如何处理数据库的并行处理问题。下面来看一下,为什么并行处理问题那么重要,同时看一下可实现的选项 决策4:处理数据库事务处理的并行问题 差不多所有的企业应用程序都需要多用户和多个后台进程并行的更新数据库。2个数据库 处理事务同时访问同时访问同一个数据是很正常的,但是这种情况很可能引起数据库中的数据不一致或者引起应用程序的不正常。由于大部分的应用程序都需要处理多个处理事务并行访问同一个数据,则它可以影响到业务和持久层的设计。 无论你是使用EJB还是轻量级构架,你的程序必须可以并行访问共享数据。EJB2要求使用供应商提供的特殊扩充接口来实现并行,然而与此不同的是,JDO和Hibernate可以直接支持大部分并行机制。更重要的是,使用JDO和Hibernate不仅只配置简单,而且只需要少量的代码就可以实现了。 在这样主要介绍几种“并行更新数据库处理事务”的选项的概要,这些事务处理和用户的输入无关。下一章,我主要介绍一下如何在应用程序级长时间的并行更新数据库处理事务,这种处理事务会与用户输入有关,而且是由一系列的数据库事务处理组成的。 独立数据库事务 这种方法也许听起来非常的简单,但问题是这种处理方式有时候会降低性能,因为如何实现对事务的孤立是由数据库来决定的。为了这个原因,许多应用程序都避免使用它,而采用optimistic或者pessimistic 所锁,这会在下面讲到。 开放式锁定 用开放式锁定机制来锁定那些直接执行SQL语句的应用程序是非常简单的。但是,用持久层构架(比如JDO和Hibernate)实现更容易,因为他们已经提供了开放式锁定机制——在配置选项中。一旦在配置选项中,选中了这种方式,持久层构架会自动的生成SQL的UPDATE语句来完成版本检查的任务。开放式锁定的名字来源于一种假设的情况,在这种情况下:并发更新的机会非常少,而且程序只能检测、覆盖这些数据而不能防止这种事情的发生。另外一种可选的途径是用保守式锁定,使用他的假设条件是:并发更新肯定会发生,而且必须被禁止。 保守式锁定 除了可以处理单个数据库事务并行问题,常常你还需要处理多数据库事务的并行问题。 决策5:在长事务下处理并发访问 独立事务、开放式锁定、和保守式锁定只能用在单个数据库事务上的,然而,许多的程序需要 长时间的 在多个数据库事务之间 读取或者更新 共享数据。比如,有一种情况描述的是 怎样实现 用户编辑命令,这和很多的进程有关,这些进程可能会运行 很长的时间,而且它由 多个数据库事务组成。因为数据可能会被 一个数据库事务 读取,而又被 另外一个数据库事务 修改了,那么程序必须对 共享数据的并发访问 进行不同的处理。这样就必须使用 开放式锁定设计模式或 者保守是锁定设计模式,关于这两种模式会在Fowler的 Patterns of Enterprise Application Architecutre中详细介绍。 开放式脱机锁定 因为开放式脱机锁定模式只有在用户进行保存修改过的数据时才可以检测,所以它只有在不成为客户的累赘的时候,才可以很好的运行。但如果情况是:客户必须要撤销几个操作的话,那么就会因为这种锁定模式而非常苦恼,那么更好的一种选择是用保守式脱机锁定。 保守式脱机锁定 |