zoukankan      html  css  js  c++  java
  • J2EE项目中的数据持久层设计

    何谓“持久化”
    持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。

    何谓“持久层”
    持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。

    何谓“对象数据映射(ORM)”
    ORM-Object/Relational Mapper,即“对象-关系型数据映射组件”。对于O/R,即 Object(对象)和 Relational(关系型数据),表示必须同时使用面向对象和关系型数据进行开发。

    备注:建模领域中的 ORM 为 Object/Role Modeling(对象角色建模)。另外这里是“O/R Mapper”而非“O/R Mapping”。相对来讲,O/R Mapping 描述的是一种设计思想或者实现机制,而 O/R Mapper指以O/R原理设计的持久化框架(Framework),包括 O/R机制还有 SQL自生成,事务处理,Cache管理等。

    以前本坛有位道友专门写了一篇持久层的文章,还有图片,可惜图片现在不在了。

    “持久化”这个概念是和“暂时”等概念相对的,数据在计算机中有一般有两个存储地,内存为暂存,因为电源关机就会数据丢失,如果需要反复使用,就要持久保存,实现持久化了。

    持久化现在有多个途径,如数据库和文件等。

    持久层是J2EE中实现持久化的一个层次,由于它和数据库等具体技术打交道,而且不同数据库提供的接口不一致,因此,有4个指标来衡量持久化技术的选择:

    1. 性能 --> 高性能,最好能直接使用具体数据库特性

    2. 通用 --> 可移植,在不同数据库之间移植
    3. 方便 --> 使用方便,o/R mapping非常方便
    4. 安全 --> 事务机制好,很好地支持CAID等。

    数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称.
    数据模型可以是任何数据结构或对象模型,存储模型可以是关系模型、XML、二进制流等。
    cmp和Hibernate只是对象模型到关系模型之间转换的不同实现。只不过对象模型和关系模型应用广泛,所以就会误认为数据持久化就是对象模型到关系型数据库的转换罢了。


    除了 ORM 技术,还有以下几种持久化技术

    主动域对象模式
    它是在实现中封装了关系数据模型和数据访问细节的一种形式。在 J2EE 架构中,EJB 组件分为会话 EJB 和实体 EJB。会话 EJB 通常实现业务逻辑,而实体 EJB 表示业务实体。实体 EJB 又分为两种:由 EJB 本身管理持久化,即 BMP(Bean-Managed Persistence);有 EJB 容器管理持久化,即 CMP(Container-Managed Persistence)。BM P就是主动域对象模式的一个例子,BMP 表示由实体 EJB 自身管理数据访问细节。
    主动域对象本身位于业务逻辑层,因此采用主动域对象模式时,整个应用仍然是三层应用结构,并没有从业务逻辑层分离出独立的持久化层。

    JDO 模式
    Java Data Objects(JDO)是 SUN 公司制定的描述对象持久化语义的标准API。严格的说,JDO 并不是对象-关系映射接口,因为它支持把对象持久化到任意一种存储系统中,包括 关系数据库、面向对象的数据库、基于 XML 的数据库,以及其他专有存储系统。由于关系数据库是目前最流行的存储系统,许多 JDO 的实现都包含了对象-关系映射服务。

    CMP 模式
    在 J2EE 架构中,CMP(Container-Managed Persistence)表示由 EJB 容器来管理实体 EJB 的持久化,EJB 容器封装了对象-关系的映射及数据访问细节。CMP 和 ORM 的相似之处在于,两者都提供对象-关系映射服务,都把对象持久化的任务从业务逻辑中分离出来。区别在于 CMP 负责持久化实体 EJB 组件,而 ORM 负责持久化 POJO,它是普通的基于 Java Bean 形式的实体域对象。

    一般把基于 Java Bean 形式的实体域对象称为 POJO(Plain Old Java Object),意为又普通又古老的 Java 对象的意思。随着各种 ORM 映射工具的日趋成熟和流行,POJO有重现光彩,它和基于 CMP 的实体 EJB 相比,即简单又具有很高的可移植性,因此联合使用 ORM 映射工具和 POJO,已经成为一种越来越受欢迎的且用来取代 CMP 的持久化方案。POJO 的缺点就是无法做远程调用,不支持分布式计算。


    为什么要做持久化和ORM设计

    在目前的企业应用系统设计中,MVC,即 Model(模型)- View(视图)- Control(控制)为主要的系统架构模式。MVC 中的 Model 包含了复杂的业务逻辑和数据逻辑,以及数据存取机制(如 JDBC的连接、SQL生成和Statement创建、还有ResultSet结果集的读取等)等。将这些复杂的业务逻辑和数据逻辑分离,以将系统的紧耦合关系转化为松耦合关系(即解耦合),是降低系统耦合度迫切要做的,也是持久化要做的工作。MVC 模式实现了架构上将表现层(即View)和数据处理层(即Model)分离的解耦合,而持久化的设计则实现了数据处理层内部的业务逻辑和数据逻辑分离的解耦合。而 ORM 作为持久化设计中的最重要也最复杂的技术,也是目前业界热点技术。

    简单来说,按通常的系统设计,使用 JDBC 操作数据库,业务处理逻辑和数据存取逻辑是混杂在一起的。
    一般基本都是如下几个步骤:
    1、建立数据库连接,获得 Connection 对象。
    2、根据用户的输入组装查询 SQL 语句。
    3、根据 SQL 语句建立 Statement 对象 或者 PreparedStatement 对象。
    4、用 Connection 对象执行 SQL语句,获得结果集 ResultSet 对象。
    5、然后一条一条读取结果集 ResultSet 对象中的数据。
    6、根据读取到的数据,按特定的业务逻辑进行计算。
    7、根据计算得到的结果再组装更新 SQL 语句。
    8、再使用 Connection 对象执行更新 SQL 语句,以更新数据库中的数据。
    7、最后依次关闭各个 Statement 对象和 Connection 对象。

    由上可看出代码逻辑非常复杂,这还不包括某条语句执行失败的处理逻辑。其中的业务处理逻辑和数据存取逻辑完全混杂在一块。而一个完整的系统要包含成千上万个这样重复的而又混杂的处理过程,假如要对其中某些业务逻辑或者一些相关联的业务流程做修改,要改动的代码量将不可想象。另一方面,假如要换数据库产品或者运行环境也可能是个不可能完成的任务。而用户的运行环境和要求却千差万别,我们不可能为每一个用户每一种运行环境设计一套一样的系统。
    所以就要将一样的处理代码即业务逻辑和可能不一样的处理即数据存取逻辑分离开来,另一方面,关系型数据库中的数据基本都是以一行行的数据进行存取的,而程序运行却是一个个对象进行处理,而目前大部分数据库驱动技术(如ADO.NET、JDBC、ODBC等等)均是以行集的结果集一条条进行处理的。所以为解决这一困难,就出现 ORM 这一个对象和数据之间映射技术。

    举例来说,比如要完成一个购物打折促销的程序,用 ORM 思想将如下实现(引自《深入浅出Hibernate》):
    业务逻辑如下:
    public Double calcAmount(String customerid, double amount) 
    {
        // 根据客户ID获得客户记录
        Customer customer = CustomerManager.getCustomer(custmerid); 
        // 根据客户等级获得打折规则
        Promotion promotion = PromotionManager.getPromotion(customer.getLevel()); 
        // 累积客户总消费额,并保存累计结果
        customer.setSumAmount(customer.getSumAmount().add(amount); 
        CustomerManager.save(customer); 
        // 返回打折后的金额
        return amount.multiply(protomtion.getRatio()); 
    }
    这样代码就非常清晰了,而且与数据存取逻辑完全分离。设计业务逻辑代码的时候完全不需要考虑数据库JDBC的那些千篇一律的操作,而将它交给 CustomerManager 和 PromotionManager 两个类去完成。这就是一个简单的 ORM 设计,实际的 ORM 实现框架比这个要复杂的多。


    目前有哪些流行的 ORM 产品
    目前众多厂商和开源社区都提供了持久层框架的实现,常见的有
    Apache OJB (http://db.apache.org/ojb/
    Cayenne (http://objectstyle.org/cayenne/
    Jaxor (http://jaxor.sourceforge.net/
    Hibernate (http://www.hibernate.org/
    iBatis (http://www.ibatis.com/
    jRelationalFramework (http://ijf.sourceforge.net/
    mirage (http://itor.cq2.org/en/oss/mirage/toon
    SMYLE (http://www.drjava.de/smyle
    TopLink (http://otn.oracle.com/products/ias/toplink/index.html
    其中 TopLink 是 Oracle 的商业产品,其他均为开源项目。

    其中 Hibernate 的轻量级 ORM 模型逐步确立了在 Java ORM 架构中领导地位,甚至取代复杂而又繁琐的 EJB 模型而成为事实上的 Java ORM 工业标准。而且其中的许多设计均被 J2EE 标准组织吸纳而成为最新 EJB 3.0 规范的标准,这也是开源项目影响工业领域标准的有力见证。


    参考文献:1、《深入浅出Hibernate》
             2、《精通Hibernate:Java对象持久化技术详解》

    据持久层的设计目标是为整个项目提供一个高层、统一、安全和并发的数据持久机制。完成对各种数据进行持久化的编程工作,并为系统业务逻辑层提供服务。数据持久层提供了数据访问方法,能够使其它程序员避免手工编写程序访问数据持久层(Persistene layer),使其专注于业务逻辑的开发,并且能够在不同项目中重用映射框架,大大简化了数据增、删、改、查等功能的开发过程,同时又不丧失多层结构的天然优势,继承延续J2EE特有的可伸缩性和可扩展性。

    1 数据持久层及ORM映射框架

    笔者从事的项目中的数据持久层,是基于J2EE体系结构,并采用了Hibernate作为持久映射框架。

    Hibernate是一种新的ORM映射工具,是JDBC的轻量级的对象封装。Hibernate可以用在JDBC可以使用的任何场合,例如Java应用程序的数据库访问代码,DAO接口的实现类,甚至可以是BMP里面的访问数据库的代码。Hibernate不仅提供了从Java类到数据表之间的映射,也提供了数据查询和恢复机制。相对于使用JDBC和SQL来手工操作数据库,使用Hibernate,可以大大减少操作数据库的工作量。

    Hibernate是一个和JDBC密切关联的、独立的对象持久层框架,可以搭配各种App Server、Web Server、EJB Container共同使用,Hibernate的兼容性仅同JDBC驱动、底层数据库产品间有一定的关系,但是和使用它的Java程序、App Server没有任何关系,也不存在兼容性问题。而且事实表明Hibernate可以和多种Web服务器或者应用服务器良好集成,如今已经支持几乎所有的流行的数据库服务器(达16种)。 在较为常用的数据持久方案中,Hibernate无疑是最优秀的,下面是对各种持久方案的比较。

    ¨ 流行的数据持久层架构: Business Layer <-> Session Bean <-> Entity Bean <-> DB

    ¨ 为了解决性能障碍的替代架构: Business Layer <-> DAO <-> JDBC <-> DB

    ¨ 使用Hibernate来提高上面架构的开发效率的架构: Business Layer <-> DAO <-> Hibernate <-> DB

    我们就上面3个架构来作如下分析。

    (1)内存消耗:采用JDBC的架构无疑是最省内存的,Hibernate的架构次之,EB的架构最差。

    (2)运行效率:如果JDBC的代码写的非常优化,那么JDBC架构运行效率最高,但是实际项目中,这一点几乎做不到,这需要程序员非常精通JDBC,运用Batch语句,调整PreapredStatement的Batch Size和Fetch Size等参数,以及在必要的情况下采用结果集cache等等。而一般情况下程序员是做不到这一点的。因此Hibernate架构表现出最快的运行效率。EB的架构效率会差的很远。

    (3)开发效率:在有Eclipse、JBuilder等开发工具的支持下,对于简单的项目,EB架构开发效率最高,JDBC次之,Hibernate最差。但是在大的项目,特别是持久层关系映射很复杂的情况下,Hibernate效率高的惊人,JDBC次之,而EB架构很可能会失败。

    2 数据持久层设计

    复杂性是应用开发过程中最令人头疼的一个问题。每当在一个应用中增加一个功能时,它的复杂性通常呈几何级的增长。这种复杂性往往导致程序的开发无法再继续下去。这也是现在为什么许多应用只有Beta版本而没有正式版的原因。 

    专家将应用开发过程产生的复杂性分为两类,即非本质的(accidental)和本质的(essential)。

    本质的复杂性是对于解决目标问题所必然产生的复杂性,非本质的复杂性是由于选择了不适当的开发工具和设计工具而产生的复杂性。对于一个功能确定的程序来讲,本质的复杂性是确定的,而非本质的复杂性则是没有限制的。

    因此,一个应用的开发要想较顺利地取得成功,就需要尽可能地减少非本质的复杂性。 设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式,也会使新系统开发者更加容易理解其设计思路。

    衡量一个系统优秀与否的关键因素,除了能够满足用户需求外还有如下方面:

    首先是灵活性。灵活性意指这种结构或模式不依赖于任何实际应用,应该与操作系统、应用程序无关。提供独立的结构,可以提供最大的重用。

    其次是可扩展性。随着业务的扩展,新的业务不断增加,业务逻辑自然增加,系统必然会进行修改或添加相应功能模块。

    再次是可配置性。最后是安全性。 数据持久层的设计采纳了多种设计模式,最大限度的降低了系统内部各模块、子系统间的耦合性,使得系统相对易于扩展,并且能够在进行改变时,保证持久层的业务逻辑层相对稳定,基本不需要因持久层的调整改变而进行逻辑层的变动。

    笔者在项目中采用了如下设计模式。

    2.1 整体架构——MVC模式(模型-视图-控制器)

    ¨ 模型(Model):模型包含完成任务所需要的所有的行为和数据。在数据持久层中,模型即为值对象以及数据访问对象。

    ¨ 视图(View):数据持久层中,视图就是持久层同其它层进行数据交换的值对象(Transfer Object)和视图助手对象。

    ¨ 控制器(Controller):持久层所需的控制相对简单,因此集成到了控制代理中。 持久层整体采用MVC模式,使得整个数据持久层的实现部分与项目的业务逻辑部分隔离开来,能够实现对接口作大的修改而不需要对相应的模型进行修改。另外,持久层某子系统发生变化时,不会影响到其它子系统。有利于提高系统的稳定性、可维护性。

    2.2 值对象模式(Value Object Pattern) 值对象用来封装业务对象。相应的方法调用是设置(getter)和检索(setter)值对象。它是任意的可串行化的Java对象,当客户端Bean请求业务数据时,该Bean可以构造值对象,用属性值来填充,并按照值把它传递给客户端。 在笔者开发项目的数据持久层体系结构中,值对象主要应用在子系统间传递、交换数据(Transfer Object)和映射数据表两个方面(Persistent Object)。

    在各子系统间进行数据传递和数据交换时,使用值对象模式能够最大化地降低系统间数据传递的开销。在这种策略下传递的是对象而不再是一个个的有意义的数据,使得系统在进行扩充、修改时,各子系统间数据传递部分不会受到影响,因为各子系统仅需要关心是否有值对象被传递,而并不去关心传递的到底是什么数据。

    在映射数据库表时,值对象类及其子类所构成的树形结构被用来映射一个数据库表,该继承树通过XML配置文件对应数据库中的单个表,这使得最底层的关系型的数据库表结构能够面向对象模型所隐藏,另外,由于面向对象设计方法中类的可继承性,采用继承树对应一个表的策略使得该映射策略极易扩展,并且能够将一个复杂的数据表转化成若干简单的值对象来表示,提高了系统的可维护性和可修改性。

    2.3 数据访问对象(DAO) 根据数据源不同,数据访问也不同。根据存储的类型(关系数据库、面向对象数据库等)和供应商不同,持久性存储(比如数据库)的访问差别也很大。当业务组件或表示组件需要访问某数据源时,它们可以使用合适的API来获得连接性,以及操作该数据源。但是在这些组件中包含连接性和数据访问代码会引入这些组件及数据源实现之间的紧密耦合。组件中这类代码依赖性使应用程序从某种数据源迁移到其它种类的数据源将变得非常麻烦和困难,当数据源变化时,组件也需要改变,以便于能够处理新类型的数据源。 笔者开发项目的数据持久层使用数据访问对象(DAO)来抽象和封装所有对数据源的访问。DAO管理着与数据源的连接以便于检索和存储数据,DAO实现了用来操作数据源的访问机制,内部封装了对Hibenernate数据操纵、事务处理、会话管理等API的封装。外界依赖于DAO的业务组件为其客户端使用DAO提供了更简单的接口,DAO完全向客户端隐藏了数据源实现细节。由于当低层数据源实现变化时,DAO向客户端提供的接口不会变化,采用该设计模式允许DAO调整到不同的存储模式,而不会影响其客户端或业务组件,即使将来不再采用Hibernate作为关系映射框架,上层客户端也不会受到任何影响。另外,DAO还充当组件和数据源之间的适配器的角色。 数据持久层通过调整抽象工厂(Abstract Factory)模式和工厂方法(Factory Method) 模式(这二个创建型模式的实现详情参见GoF的<设计模式>),),使DAO模式达到了很高的灵活度。 当底层存储随着实现的变化而变化时,该策略可以通过使用抽象工厂模式实现。抽象工厂可以基于工厂方法实现而创建,并可使用工厂方法实现。该策略提供一个DAO的抽象工厂对象,其中该对象可以构造多种类型的具体的DAO工厂,每个工厂支持一种不同类型的持久性存储实现。一旦你获取某特定实现的具体DAO工厂,可以使用它来生成该实现中所支持和实现的DAO。

    2.4 连接池、应用级缓存及享元模式(提升系统性能)

    ¨ 缓存(Cache) 对于数据库来说,厂商的做法往往是在内存中开辟相应的区域来存储可能被多次存取的 数据和可能被多次执行的语句,以使这些数据在下次被访问时不必再次提交对DBMS的请求和那些语句在下次执行时不必再次编译。

    同样,数据持久层采用缓存技术来保存已经从数据库中检索出来的部分常用数据。客户端访问持久层时,持久层将首先访问缓存,如果能够命中则直接从缓存中提取数据,否则再向数据库发送提取数据的指令。这种设计能够大幅度地提高数据访问速度。

    ¨ 连接池(Connection Pool) 池是一个很普遍的概念,和缓冲存储有机制相近的地方,都是缩减了访问的环节,但它更注重于资源的共享。 对于访问数据库来说,建立连接的代价比较昂贵,因此,数据持久层建立了“连接池”以提高访问的性能。数据持久层把连接当作对象,整个系统启动后,连接池首先建立若干连接,访问本来需要与数据库连接的区域,都改为和池相连,池临时分配连接供访问使用,结果返回后,访问将连接交还。这种设计消除了JDBC与数据源建立连接的延时,同时在应用级提供了对数据源的并发访问。  

    ¨ 享元模式(Flyweight) 面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,数据库中的记录,如果以每条记录作为一个对象,提取几千条记录,对象数就是几千,这无疑相当耗费内存。数据持久层依据享元模式设计了若干元类,封装可以被共享的类。这种设计策略显著降低了系统的内存消耗。

    2.5 各种对象的创建模式—工厂方法(Factory Method)

    工厂方法模式将创建实例的工作与使用实例的工作分开,也就是说,让创建实例所需要的大量初始化工作从简单的构造函数中分离出去。只需要调用一个统一的方法,即可根据需要创建出各种对象的实例,对象的创建方法不再用编码到程序模块中,而是统一编写在工厂类中。这样在系统进行扩充修改时,系统的变化仅存在于工厂类内部,而绝对不会对其他对象造成影响。

  • 相关阅读:
    桟错误分析方法
    gstreamer调试命令
    sqlite的事务和锁,很透彻的讲解 【转】
    严重: Exception starting filter struts2 java.lang.NullPointerException (转载)
    eclipse 快捷键
    POJ 1099 Square Ice
    HDU 1013 Digital Roots
    HDU 1087 Super Jumping! Jumping! Jumping!(动态规划)
    HDU 1159 Common Subsequence
    HDU 1069 Monkey and Banana(动态规划)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2764367.html
Copyright © 2011-2022 走看看