`
zhangziyangup
  • 浏览: 1075919 次
文章分类
社区版块
存档分类
最新评论

持久化模式,第 1 部分: 现代 ORM 工具的策略和最佳实践,使用 Hibernate 特性在领域模型上应用面向对象原则

 
阅读更多

简介

在过去 5 到 10 年中,开发人员对企业应用程序中的实体进行持久化的方式发生了根本性变化。早期的企业应用程序使用数据库表和表之间的外键关系进行实体建模。应用程序被看作查看和查询数据库底层模型的方式。近几年,数据库中的实体建模逐渐向应用程序对象模型中的实体建模转变。现在大家已经意识到,数据库仅仅是存储对象结构所定义的持久化信息的一种机制。把建模从数据库转移到对象模型中有许多优点,包括:

  • 持久化实体与对它们执行的操作更紧密地集成
  • 有助于创建松散耦合的应用程序组件
  • 与关系数据库相比,面向对象模型支持更丰富的关系
  • 更加独立于特定的数据库平台

发生这种转变的主要原因是出现了功能强大的对象-关系映射(ORM)系统,它们支持按照与目标语言的习惯用法一致的方式访问持久化对象。Hibernate 和 TopLink 等工具大大简化了把对象模型映射到关系数据库模式的过程。

自从这些工具出现以来,使用它们的方法也有所变化。最初,许多开发人员按照使用数据库表的方式使用 ORM 工具。实体一对一地映射到数据库表。对应于主键等字段的变量在各个实体中重复出现。因为数据库不支持与实体相关联的行为,领域模型最终只具有简单的变量以及相关的 getter 和 setter 方法。这些实体的行为最终由服务或视图层实现。

在许多项目中使用 ORM 工具的经验揭示了处理这些问题的更好方法。业务领域各不相同,所以它们的领域模型和持久化方式也可能不同,但也有相同之处。本文讨论应用于不同行业的许多领域模型的最佳实践。这里提供的最佳实践有助于产生更加一致、可重用且可维护的领域模型。我们使用 Hibernate 演示这些最佳实践,但是许多概念可以应用于其他 ORM 工具。

本文分为两部分。第 1 部分讨论以下方面的一些基本概念:

  • 实现领域中的通用功能
  • 减少数据访问层中的代码重复
  • 按照一致的方式处理对实体修改的审计

第 2 部分 更深入地讨论这里介绍的一些概念,还要讨论领域模型中的性能调优。

从基础开始:对象模型

定义一个支持持久化对象的对象模型的过程与定义任何对象模型相同。首先,寻找所有对象共享的通用元素。持久化信息中有两个通用元素:惟一地标识持久化对象的方法(应该能够跨应用程序的各次执行标识对象),以及关于对象实例的审计信息。图 1 说明如何用接口和基类定义这两个概念:

图 1. 通用的接口和基类 通用的接口和基类

图 1 引入了 IdentifiableAuditable 接口,这些接口定义的 API 用来标识对象实例和设置对象实例的审计信息。还引入了 BaseEntityAuditableEntity 基类,可以根据是否需要对象的审计信息,分别从这些基类派生出具体的持久化类。

通过用这些接口定义持久化对象,就可能创建出可以应用于所有具体对象类型的抽象行为。这包括 UI 层(用来标识要执行创建、读取、更新和删除(CRUD)操作的对象)以及服务和数据层。本文的代码示例(在 下载 中可以获得完整的代码包)演示如何使用这些接口帮助执行审计和减少数据访问对象(DAO)中的代码重复。

通用的基实体

与对象不同,数据库表没有继承的概念。许多表中都有的字段(比如审计字段)必须为每个表重新定义。请牢记,可以在 Java™ 代码中使用继承,以避免这种重复出现在代码中。尽管 ORM 工具早就支持这个特性,但是 Java Persistence API 注解使之大大简化了,可以进一步减少代码重复(参见 参考资料)。

通过使用 Java 5 注解,可以用类级注解在类源代码中直接嵌入数据库映射。Java Persistence API 为此定义了一套标准注解。Hibernate 和其他工具现在支持这些注解。可以通过 @MappedSuperclass 注解使用在基类中定义的映射。只要所有数据库表对通用字段采用相同的列类型和列名,那么只需在基类中编写映射一次,就可以在所有子类中重用。清单 1 是 BaseEntity 类的一个示例:

清单 1. 用 @MappedSuperclass 定义 BaseEntity

清单 1 中的映射把 ID 字段映射到默认列名(id),并指定自动生成 ID(实现方式与数据库相关)。

注意,即使每个表使用不同的列名,仍然可能使用这些通用基字段。请考虑一种典型情况:所有数据库表的主键都是 Long 类型的,但是列名可能不一样。通过重新定义分配给特定子类的一个属性的列,仍然可以重用与 id 属性相关的代码。清单 2 演示如何重新定义与 id 属性相关联的列:

清单 2. 重新定义与 id 属性相关联的列

                @Entity
@AttributeOverride( name="id", column = @Column(name="EMPLOYEE_ID") )
public class Employee extends BaseEntity {//...}

如果不使用 Hibernate 注解,也可以重用这些基类中的代码。但是,必须映射每个具体类的字段。Hibernate 会在 Java 代码中自动使用继承的字段。

数据访问核心

顾名思义,DAO 模式封装了访问一个对象或相关对象集中的数据的逻辑。Hibernate 中的 DAO 包含 Criteria 查询和 Hibernate Query Language 查询,还有一个 Hibernate SessionFactory。所有面向数据库的逻辑都应该包含在 DAO 中,这意味着应该通过 DAO 存储和读取普通 Java 对象(POJO)和其他基本数据类型值。DAO 是企业 Java 分层体系结构中一种很典型的模式,通常通过一个服务访问 DAO。仔细研究 DAO 就会发现,它们的操作往往很相似。

为了了解 DAO 之间的共性,我们来看一些示例。清单 3 给出的两个方法根据 图 1 中定义的 Identifiable 接口中的 ID 查询 AddressEmployee

清单 3. 数据访问对象中的典型方法

EmployeeAddress 上的这些操作之间,主要的差异仅仅是操作中使用的类。查询是相同的,只是把结果转换为不同的类。其他操作(比如删除模型中的给定实体或者从模型中获取某一实体的所有实例)在 DAO 中是相似的,在不同实体之间也是相似的。因此,可以利用 Java 1.5 的泛型功能创建一个可重用的 DAO,从而构成数据访问层的核心。

泛型 DAO

泛型 DAO 模式(也称为类型安全的 DAO)对于减少数据访问层中的代码重复非常重要。如果使用 Java 1.4,也可以因通用基 DAO 而受益;这种实现不是类型安全的,也不是很简洁,但是仍然能够减少代码重复。

有几种实现泛型 DAO 的方式,其中一些方式取决于您的环境。这个示例使用依赖项注入风格,因此实现不需要为如何配置自身操心,而是假设在使用它之前它所需要的所有东西都已经注入了。其他一些方式不使用依赖项注入(更多信息参见 参考资料 中 Hibernate wiki 的链接)。依赖项注入方式的关键是,在查询数据时,要注入 DAO 将查询的实体的 Class 并定义泛型类型。

创建泛型 DAO 的第一步是定义它的一些通用操作。图 2 给出一个泛型 DAO 的接口:

图 2. 基 DAO 接口和实现 基 DAO 接口和实现

清单 4 给出这个泛型 DAO 的一些示例代码:

清单 4. 泛型 DAO 模式的示例代码

清单 4 中的方法构成了数据访问层的核心。可以直接使用这个泛型 DAO,也可以根据查询的实体的需要,从它派生出子类。可以通过一个服务直接使用这个泛型 DAO,比如清单 5 中的服务:

清单 5. 通过服务直接使用泛型 DAO

如果查询需要更复杂的数据集,那么可以从泛型 DAO 派生出子类。例如,假设希望找到在 Iowa 生活的所有全职职员。为此,需要定义一个与 Employee 相关的 DAO 方法,findIowaEmployees。如果创建一个扩展 BaseDAO 的新的 EmployeeDAOEmployeeDAO 可以执行特定的全职职员查询,还可以执行泛型 DAO 提供的所有基本查询,见清单 6:

清单 6. 从泛型 DAO 派生出子类

注意,清单 6 使用 createCriteria() 方法。它是另一个在企业应用程序的 DAO 中常常重复出现的方法。在使用泛型 DAO 的过程中,您会发现新的通用操作,可以把它们添加到泛型 DAO 中,从而增强可重用性并减少重复。第 2 部分 将详细描述其他一些通用方法,比如启用真正的分页和处理搜索参数。

审计

基本审计是以数据库为中心的应用程序中的常见特性之一。大多数应用程序都需要记录审计信息,比如何时创建了对象、谁创建了对象、何时修改了对象以及谁修改了对象。这些特性的建模并不困难。要求审计的任何对象只需要增加 4 个字段,每个字段存储一种审计信息。图 1 为包含审计字段的持久化实体提供了一个基类。这个特性比较难的部分是,决定在代码中的什么地方设置审计信息。有几个选择:

  • 生硬的方式:在需要修改审计信息的对象的任何地方,都确保用正确的值填充审计信息。这种方式有许多明显的缺陷。最主要的缺陷是,审计逻辑会在应用程序中的许多地方重复出现。即使把这一逻辑集中在一个实用程序类中,仍然必须记住在修改对象的每个地方使用这个实用程序 — 不只是在最初开发系统时,在维护系统时也必须牢记这一点。但是,在任何规模的系统中,开发人员或早或晚都会忘记这项要求。
  • 把审计逻辑放在 DAO 中:另一种方式是把审计逻辑集中在一个通用 DAO 中。通过在保存方法中添加审计逻辑,使用这个 DAO 保存的任何对象都会自动填充审计字段。这种方式在许多情况下效果很好,但是仍然有一些缺陷。一个缺陷是,这种方式假设应用程序总是使用通用 DAO 的保存方法来保存数据。但是,实际情况不一定总是这样的,那么前面的问题就又出现了:必须记住添加审计逻辑。另一个问题更严重:这个解决方案忽略了 ORM 工具最有用的特性之一,过渡持久化(transitive persistence)。可以使用 DAO 显式地保存 Employee 对象,但是 Hibernate 也会自动地对与它相关联的 Address 的任何修改进行持久化。在这种情况下,Employee 会填充它的审计字段,但是 Address 不会。
  • Hibernate 的 Interceptor :为了解决这个问题,需要在 Hibernate 中建立一个扩展点。每当框架保存对象时,需要在一个地方填充这些审计字段。Hibernate 通过它的 Interceptor 接口提供了这个特性。这个接口为许多 Hibernate 事件提供回调方法,包括创建、修改和删除对象。把审计逻辑放在 Hibernate 的 Interceptor 中,就可以消除重复的逻辑,而且不再需要为确保执行逻辑操心。只要由 Hibernate 负责保存数据,就一定会执行审计逻辑。

实现的细节

Hibernate 有一个 EmptyInterceptor 类,它为 Interceptor 接口中的十几个回调方法提供了空的实现。通过这个类添加审计信息是非常好的方式。在清单 7 中的实现中,只有两个方法与审计相关:onSave(当把新对象刷新到数据库时调用这个方法)和 onFlushDirty(当 Hibernate 把更新过的(脏)对象刷新到数据库时调用这个方法):

清单 7. 扩展 EmptyInterceptor

这两个事件都在 Hibernate 已经决定要保存对象的哪些属性以及那些属性的值之后调用。传递给这些方法的参数包括属性名的 String 数组和属性值的 Object 数组。因为 Hibernate 已经决定了要保存的值,所以直接更新对象不会取得想要的效果。实际上,更新对象不会对最终发送到数据库的值产生影响。所以,实际上需要更新属性值数组中的元素。尽管这种更新数组的方式有点儿笨拙,但是实现仍然相当简单明了。只需循环遍历属性名数组,寻找审计字段。找到这些字段之后,用对应的索引更新值数组。还有最后一点细节需要注意:这些回调方法返回一个布尔值。如果修改了对象的状态,方法就需要返回 true。如果没有进行修改,就应该返回 false。清单 8 中的示例代码显示这一逻辑:

清单 8. 更新审计字段的 Interceptor 回调方法

最后一步是帮助强制使用统一的审计字段名称,并确保数据库实体具有这些字段。Auditable 接口是实现这个目标的最容易、最好的方法,但是它看上去有点儿奇怪。尽管为这些字段提供了 getter 和 setter 方法,但是审计代码中实际上不使用这些方法。但是,通过让实体实现 Auditable 接口,可以显著减少在开发持久化类时需要的代码量。

第 1 部分结束语

本文主要关注使用 Hibernate 特性在领域模型上应用基本的面向对象原则。与所有模式和最佳实践一样,您应该根据自己的环境评估这里描述的解决方案,并相应地进行调整。泛型 DAO 能够提供很强的灵活性。不同的数据库结构、技术和业务需求具有不同的通用功能,可以把这些功能转移到泛型 DAO 的通用代码中。不同的应用程序可能需要不同的审计信息(ATM 机领域实体事务的审计信息显然与职员地址的审计信息不一样)。无论处于什么上下文,最佳实践是相同的。

本系列的 第 2 部分 进一步讨论构造数据模型方面的最佳实践,包括利用 Hibernate 的多态性、泛型 DAO 的其他有用特性以及数据模型的性能调优。

下载

描述 名字 大小 下载方法 本文的示例代码
PatternsOfPersistenceCode.zip 16KB HTTP

分享到:
评论

相关推荐

    Hibernate实战(第2版 中文高清版)

    第一部分 从Hibernate和EJB 3.0开始  第1章 理解对象/关系持久化   1.1 什么是持久化   1.1.1 关系数据库   1.1.2 理解SQL   1.1.3 在Java中使用SQL   1.1.4 面向对象应用程序中的持久化   1.2 范式不...

    hibernate-prj1

    1、Hibernate 是一个 ORM(Object-Relational Mapping)框架,用于把对象模型 表示的对象映射到基于 SQL 的关系模型数据结构中去,采用完全面向对象的 方式来操作数据库; 2、Hibernate 的主要作用是简化应用的数据...

    Hibernate框架包

    持久化上下文大体内容:1、ORM元数据,JPA支持annotion(注解)或xml两种形式描述对象/关系映射 2、实体操作API,实现对实体对象的CRUD操作 3、查询语言,约定了面向对象的查询语言JPQL(javaPersistence Query ...

    Hibernate数据持久层框架.rar

    是一款面向对象的ORM框架,JPA不需要关心数据库字段,通过注解将数据库表与实体完成映射,在java中的操作只需要对实体进行操作即可。 什么是ORM? ORM(Object-Relational Mapping) 表示对象关系映射。在面向对象...

    NHibernate参考文档 - 符合.Net习惯的关系数据库持久化

    在当今的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦、浪费时间的。NHibernate是一个面向.NET环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语...

    Hibernate 中文API文档

    对于那些在基于Java的中间层应用中,它们实现面向对象的业务模型和商业逻辑的应用,Hibernate是最有用的。不管怎样,Hibernate一定可以帮助你消除或者包装那些针对特定厂商的SQL代码,并且帮你把结果集从表格式的...

    hibernate中文帮助文档html

    对于那些在基于Java的中间层应用中,它们实现面向对象的业务模型和商业逻辑的应用,Hibernate是最有用的。不管怎样,Hibernate一定可以帮助你消除或者包装那些针对特定厂商的SQL代码,并且帮你把结果集从表格式的...

    Hibernate3.0中文手册.rar

    对于那些在基于Java的中间层应用中,它们实现面向对象的业务模型和商业逻辑的应用,Hibernate是最有用的。不管怎样,Hibernate一定可以帮助你消除或者包装那些针对特定厂商的SQL代码,并且帮你把结果集从表格式的...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    所使用的应用服务器是jboss 5.1和weblogic 11g,详细介绍了这两种应用服务器的安装和使用,以及如何在两大主流服务器上安装、部署java ee应用。  本书内容主要包括三部分,第一部分介绍java ee开发的基础知识,...

    Spring面试题

    在对由三部分组成的 Spring 系列 的第 1 部分进行总结时,我使用了一个示例,演示了如何通过 Spring IOC 容器注入应用程序的依赖关系(而不是将它们构建进来)。 我用开启在线信用帐户的用例作为起点。对于该实现,...

    Hibernate中文参考文档

    Hibernate是一个面向Java环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。 ...

    ssh(structs,spring,hibernate)框架中的上传下载

     您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer等工具或手工的方式,编写Hibernate的领域对象和映射文件。其中对应T_FILE表的领域对象Tfile.java为:  代码 1 领域对象Tfile 1. ...

    达内java培训目录

    掌握面向对象的基本原则以及在编程实践中的意义;掌握Java面向对象编程基本实现原理。 实训项目一:Tetris项目开发 第二阶段(以T-DMS V1项目贯穿) JDK核心API 语言核心包、异常处理、常用工具包、集合框架。 ...

    java面试题

    Hibernate持久化:Hibernate根据定义的映射规则将对象持久化保存到数据库,这就实现了对象的持久化。 Spring由那几个模块组成? 答:Spring主要由7个模块组成: 1:Spring核心容器:提供了Spring框架的基本功能 2...

    从J2SE到J2EE知识点介绍

    (二) 持久化层 193 (三) ORM介绍 193 (四) Hibernate 核心接口 194 1. Session接口 194 2. SessionFactory接口 194 3. Configuration类 194 4. Transaction接口 195 5. Query和Criteria接口 195 (五) Hibernate的...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    Java是一门各方面性能都很好的编程语言,它的基本特点是简单、面向对象、分布式、解释的、健壮的、安全的、结构中立的、可移植的、性能很优异的、多线程的、动态的,特别适合在Internet环境上开发的应用系统。...

    jdbc基础和参考

    1.hibernate提供的更面向对象的一种查询方式。 准备工作: 1.java中的POJO对象存在 2.数据库,表得存在 3.hibernate的配置文件(hibernate.cfg.xml)得存在 4.POJO.hbm.xml文件存在 5.hibernate的jar包以及数据库的...

    低清版 大型门户网站是这样炼成的.pdf

    第5章 hibernate 3持久化技术实践与性能优化 271 5.1 操作持久化对象的常用方法介绍 271 5.1.1 使用session的begintransaction()方法 272 5.1.2 使用session的close()方法 273 5.1.3 使用session的connection()...

    Hibernate中文API大全

    在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦、浪费时间的。Hibernate是一个面向Java环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语...

    网上体育商城的设计与实现毕业设计答辩PPT.pptx

    简单来说,Hibernate是一个轻量级的ORM(对象关系映射)解决方案或java工具、Hibernate框架进行简化,用java开发应用程序与数据库的交互。Hibernate框架的主要优点是:比起JDBC,Hibernate没有很多复杂的SQL语句需要...

Global site tag (gtag.js) - Google Analytics