65.9K
CodeProject 正在变化。 阅读更多。
Home

停止黑客攻击,开始绘制地图:对象关系映射 101

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.43/5 (5投票s)

2009年1月1日

CPOL

15分钟阅读

viewsIcon

60339

了解如何弥合关系设计(模型)与对象设计(模型)之间的差距,并使用 Telerik OpenAccess ORM 提高您的工作效率。

执行摘要

随着行业从三层模型转向 N 层模型,对象关系阻抗不匹配变得越来越普遍。对象关系映射器 (ORM) 的存在是为了尽可能地弥合这一差距。Telerik 的 OpenAccess ORM 是一款工业级 ORM,能够满足所有现代应用程序的需求。它提供了一个向导,支持正向和反向映射到所有主流数据库,包括 Microsoft SQL Server,具有紧密的 Visual Studio 集成、LINQ 支持、透明持久化和延迟加载,可在中等信任环境中运行,并通过其 Fetch Plans二级缓存 提供完全可扩展的架构。

目录

三层模型

当今的业务应用程序都将数据访问作为其核心功能的一部分。随着关系数据库服务器在 20 年前越来越受欢迎,行业从单层(大型机)模型转向了客户端-服务器模型,在该模型中,客户端负责表示层逻辑和大部分业务逻辑,而服务器负责数据存储和部分业务逻辑(以存储过程的形式)。到 20 世纪 90 年代初,由于维护成本高以及缺乏关注点分离,该模型开始失效,我们转向了如图 1 所示的三层架构。

图 1. 三层架构。(来源:维基百科)

image001.jpg

三层架构强制对三个主要关注点进行逻辑(有时是物理)分离

  • 表示层 — 用户界面组件。
  • 业务逻辑 — 处理用户请求的命令,进行逻辑决策(如计算)。从数据层检索数据。
  • 数据存储和检索 — 系统的存储和检索数据,并将数据传递给业务层进行处理,并最终由表示层呈现给用户。理论上,这一层可以是文件存储、XML 或关系数据库,但关系数据库是迄今为止最常见的用法。

分离逻辑的目标是双重的。首先,通过让数据库服务器仅专注于数据库存储和检索来提高性能。特定硬件和拓扑(如 RAID)用于数据库存储和访问,这与“应用程序服务器”或业务对象和逻辑中间层不同。此外,随着客户端机器的强大,将 UI 处理推送到客户端是合理的。

其次是关注点分离原则。通过分离系统的逻辑,您可以更轻松地维护整个系统,重用代码,并将相关逻辑和代码保留在一个位置。

N 层模型

到 20 世纪 90 年代后期,行业将三层模型扩展到多层方法。该模型在逻辑上是相同的,但迫使其发生变化的是互联网成为了许多应用程序的重要组成部分。

Web 服务(以及后来的 REST 数据)已越来越多地集成到应用程序中。因此,数据层通常被拆分为数据存储层(数据库服务器)和数据访问层或层(DAL)。在非常复杂的系统中,还会添加一个额外的包装器层来统一对数据库和 Web 服务的访问。Web 浏览器远不如传统的客户端层应用程序强大,用户界面逻辑在浏览器(使用 JavaScript)和服务器(使用 Web 服务器 UI 渲染逻辑,如 ASP 或 PHP)之间进行了拆分。

随着所有主要的数据库供应商和开源数据库都添加了存储过程,层开始变得更加模糊。这使得一部分业务逻辑从业务层传播到数据库层,从而创建了层中层。例如,Microsoft Transaction Server(对象请求代理或 ORB)中的业务组件是逻辑业务层;然而,它很可能调用存储过程,而存储过程是数据库层内的逻辑业务层。

随着互联网、技术创新和服务导致层变得越来越模糊,三层模型演变成了如图 2 所示的 N 层模型示例。

图 2. N 层架构。(来源:MS Patterns and Practices)

image002.jpg

N 层架构的问题

N 层架构取得了巨大成功。当今大多数复杂的应用程序都使用某种形式的 N 层模型。多年前,为了支持这种模型,企业会围绕以数据为中心的业务应用程序设置多达五个不同的职位头衔。职位头衔包括:

  • 数据建模师
  • 数据库管理员
  • SQL 程序员
  • 对象建模师
  • 应用程序开发人员

数据建模师负责设计物理表和关系。DBA 负责创建表、维护表,并制定索引和物理磁盘策略以及维护计划。对象建模师负责构建对象模型(或 API)并将行为映射到方法。SQL 程序员会与对象建模师和应用程序开发人员合作,并在 DBA 的指导下编写存储过程和视图。应用程序开发人员将使用对象模型组件并“粘合”整个应用程序。

那是过去。虽然一些大型组织仍然这样开发,但 20 世纪 90 年代后期 RAD 工具的出现、本世纪初的敏捷/XP 运动、.com 热潮,以及最终的外包和预算削减,大多数公司不再以这种方式组织。许多小型公司只有一名“数据库专家”(如果有的话)和一名“代码专家”。有些公司只有一个人。然后,公司将大部分建模和过程创建推给“数据库专家”和应用程序开发人员。

随着预算削减、团队规模缩小以及跨职能团队的兴起,从事“数据库工作”的人员不足。开发人员抱怨他们在应用程序的数据库访问代码上花费了超过 30% 的时间。这只会加剧对象-关系阻抗不匹配。

对象-关系阻抗不匹配

基于数学的数据库规范化理论与基于软件工程原理的面向对象理论采用不同的策略。数据库表对数据进行建模并规定存储技术。对象对数据和行为进行建模。问题在于数据库的设计方式与对象模型的设计方式之间存在细微的差别。将数据库表直接映射到对象的做法会导致著名的“对象-关系阻抗不匹配”。

为了演示阻抗不匹配,让我们看一个例子。这个样本是由 Scott Ambler 在更详细的阻抗不匹配讨论中设计的。图 3 是一个简单的数据库模型图。有四个数据库表:Customer 表、Address 表以及一个名为 CustomerAddress 的多对多表,将它们链接在一起,表示一个客户可以有多个地址,并且地址是可重用的,甚至可以在客户之间共享。最后,还有一个用于 States 的支持或“查找”表,表示 States 和 Addresses 之间的一对多关系。

图 3. 数据库模型。来源:http://www.agiledata.org/essays/impedanceMismatch.html

image003.jpg

现在让我们考虑一个将与此数据交互的对象模型。在图 4 中,我们有四个对象:Customer 对象、Address 对象和支持性的 State 对象。请注意,由于 Customer 和 Address 对象之间没有“居住在”和 1..* 箭头符号,因此无需对“多对多”数据库表进行建模。这将指示 Customer 对象将与 Address 集合相关联。此外,我们还有一个 ZipCode 对象,该对象在数据库模型中不存在。此对象用于验证和格式化,这些行为在数据库中没有建模的必要。

图 4. 对象模型。来源:http://www.agiledata.org/essays/impedanceMismatch.html

image004.jpg

请注意图 3 和图 4 之间的细微差别?它们各有 4 个对象,其中一些名称相同(Customer、Address 和 State)并且看起来相似,但 ZipCode 对象与数据库模型中表示的 ZipCode 非常不同。这是因为对象模型结构化了数据和行为,而数据库模型仅对数据进行建模。触发器不被视为行为,它更多地与数据更新和流有关。

数据访问层 (DAL)

阻抗不匹配没有简单的解决方案,数据库规范化理论和面向对象理论之间存在根本差异。为了尽可能地弥合差距并减少为数据访问编写的代码量,开发人员已经开始编写数据访问层(DAL)。DAL 的目标是将数据访问代码与对象模型解耦,允许对象模型根据其行为进行演化,并使数据库根据其特定需求进行演化。DAL 应具备以下特征:

  • 完全独立于对象模型。换句话说,DAL 不应对对象模型施加任何约束。
  • DAL 应隐藏对象模型的所有数据访问代码。这有时被称为持久化无关性。您的对象模型不应关心您使用的是原始 SQL、存储过程还是 ORM。如果您的业务(域)对象中存在存储过程或 SQL 的名称,则您违反了这一点。
  • DAL 应能够被替换,而影响最小或没有影响。

DAL 的问题在于编写它们非常耗时,并且应用程序之间不容易重用。这就是 ORM 的用武之地。

对象关系映射 (ORM)

要解决 DAL 中的阻抗不匹配问题,您需要理解并实现将对象映射到关系数据库表的流程。类属性将映射到数据库表中的零个、一个或多个列。如果存在正确的映射和持久化无关性,开发人员应该只关心处理域模型中的对象,而 DAL 将处理其余部分。实现这一目标的一种方法是使用对象关系映射器或 ORM。

ORM 是一种自动化方法,通过将域对象映射到数据库表来根据您的数据库和对象模型创建您的 DAL。维基百科 将 ORM 定义为:“一种将关系数据库和面向对象编程语言中不兼容的类型系统之间的数据转换的编程技术。这实际上创建了一个‘虚拟对象数据库’,可以在编程语言内部使用。” ORM 必须提供一种将数据库表映射到域对象的功能,通常是设计界面或向导。此映射位于您的数据库和域模型之间,独立于源代码和数据库。然后,ORM 运行时会将域模型针对映射发出的命令转换为后端数据库检索和 SQL 语句。映射允许应用程序无缝处理多个不同的数据库模型,甚至数据库。

ORM 的映射消除了开发人员手动编写整个 DAL 的需要。一个好的 ORM 将通过其映射和持久化对象消除 90% 的 DAL 代码。由于开发人员倾向于将 30% 的代码用于编写 DAL,因此 ORM 将节省超过 25% 的总应用程序代码。由于 DAL 是由 ORM 生成的,而不是手动编写的,因此它是无错误的,并且始终符合产品标准,如果产品发生变化,这些标准更容易在未来进行更改。

在映射方面,ORM 可以采取两种方法:正向映射和反向映射。正向映射将采用您已有的对象模型,然后从中创建数据库模式。反向映射是从已有的数据库创建一组对象的表的过程。大多数 ORM 将支持正向或反向映射,有些则同时支持这两种方法。由于对象模型与适当的数据模型之间存在差异(如图 3 和图 4 所示),因此自动的一对一映射可能不是首选,大多数 ORM 会允许用户修改映射。

除了映射和命令之外,ORM 还必须提供一种方式,让您的域对象向 DAL 发出与数据库无关的命令,让 DAL 将该命令转换为相应的 SQL,然后将状态(在更新等情况下)、对象或对象集合返回给请求的域对象。这必须以透明的方式完成,您的域中的对象需要对 DAL 后面发生的事情(持久化无关性)一无所知。如今,一种流行的方法是使用 ORM LINQ 提供程序或 ORM 特定对象查询语言 (OQL)。最重要的是,您以无关的方式查询您的 DAL。例如,考虑以下针对流行 ORM 的简单 LINQ 查询:

var result = from o in scope.Extent<Order>()
                            where o.Customer.CustomerID.Matches("ALFKI") 
                            select o;

此代码仅与对象模型相关,特别是与 Order 对象相关,并返回一个 Order 集合,该集合按 Customer ID “ALFKI”进行过滤。进一步以该示例为例,以下代码演示了使用 ORM 根据用户输入的结果过滤 ASP.NET GridView,请注意,我们正在查询对象(再次是 Order)并将结果投影到新对象中,而无需担心数据库、连接或任何 SQL 代码。这些都由 ORM 在 DAL 中处理。

protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
    //get the selected customer id
    string customerid = DropDownList1.SelectedValue.ToString();

    //linq query
    IObjectScope scope = ObjectScopeProvider1.GetNewObjectScope();
    var result = from o in scope.Extent<Order>()
                 where o.Customer.CustomerID.Matches(customerid)
                 select new { o.OrderID, o.ShipName, o.ShipCity,
                 ShipCompany = o.Shipper.CompanyName };

    //databind the results
    GridView1.DataSource = result;
    GridView1.DataBind();
}

除了映射和命令之外,ORM 还应与 Microsoft Visual Studio(或 Eclipse)等开发 IDE 集成,提供缓存技术,与源代码版本控制软件集成,并提供可测试的框架。这将提高开发人员的生产力,同时使开发人员无需从头开始编写 DAL。

Telerik OpenAccess ORM

2008 年 11 月,Telerik 向其客户发布了 OpenAccess ORM。OpenAccess 是一款成熟的 ORM,由 Vanatec 开发,后于 2008 年秋季被 Telerik 收购。

映射

OpenAccess 与 Microsoft Visual Studio 深度集成,并提供正向和反向映射到包括 Microsoft SQL Server 和 Oracle 在内的六大数据库,并提供易于使用的向导,如图 5 所示。从图 5 中可以看出,该向导让您可以完全控制要映射的内容以及如何映射(作为类或集合)。OpenAccess 让用户完全控制对象和成员命名(或反向映射中的数据库字段和表命名)。

图 5. OpenAccess(反向)工程向导。

image005.jpg

OpenAccess 还生成一个透明持久化且在很大程度上持久化无关的 DAL[1],通过利用部分类,生成非常干净的无反射类,允许您在中等信任环境中运行。如果需要,您可以向 DAL 添加自己的附加逻辑,在需要重新生成类时不会被覆盖。这还使得与源版本控制软件的集成更加容易,并促进了可测试性和 TDD。这是一个由 OpenAccess 生成的类,它就像您创建的普通类一样。

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    //Generated by Telerik OpenAccess
    //NOTE: Field declarations and 'Object ID' class implementation are added to the
    //      'designer' file.
    //      Changes made to the 'designer' file will be overwritten by the wizard.      
    public partial class Region
    {
        //The 'no-args' constructor required by OpenAccess.
        public Region()
        {
        }
 
        [Telerik.OpenAccess.FieldAlias("regionID")]
        public int RegionID
        {

            get { return regionID; }
            set { this.regionID = value; }
        }
 
        [Telerik.OpenAccess.FieldAlias("regionDescription")]
        public string RegionDescription
        {
            get { return regionDescription; }
            set { this.regionDescription = value; }
        }
    }
}

数据访问

您可以使用标准的 OQL(对象查询语言)查询 DAL 中的对象,OpenAccess 提供了一个 OQL 浏览器工具,如图 6 所示,您可以在其中测试 OQL,甚至可以看到 OpenAccess 在后端数据源上生成的 SQL。

图 6. OQL 查询浏览器

image006.jpg

OpenAccess 通过其自动生成的 ObjectScopeProvider 提供了一流的 LINQ 支持。LINQ 支持特定于 OpenAccess,而不是后端数据库。此处显示了一个示例,在获取对 DAL 提供的 ObjectScopeProvider 的引用后,您可以在该引用上使用标准的 LINQ 查询运算符。

IObjectScope scope = ObjectScopeProvider1.GetNewObjectScope();

    var result = from o in scope.Extent<Order>()
    where o.Customer.CustomerID.Matches("ALFKI")
    select o;

如果您对 OpenAccess 通过 LINQ 或 OQL 生成的 SQL 不满意,可以通过定义获取计划来优化 OpenAccess 将生成的查询。您可以使用获取计划浏览器如图 7 所示,以图形方式定义获取计划。

图 7. 获取计划浏览器

image007.jpg

延迟加载和二级缓存

OpenAccess 还使用“延迟加载”或按需加载资源的功能,从而提高性能。OpenAccess 还有一个非常独特的缓存机制,即二级 (L2) 缓存,它减少了对后端数据库服务器的调用次数。因此,只有在检索当前不在缓存中的数据时才需要数据库访问。这提高了应用程序的效率和性能,并使您能够轻松地迁移到多层应用程序或 Web 场环境。

通过二级缓存集群,缓存的可扩展性更强。在应用程序服务器场场景中,当多个应用程序使用同一个数据库的 L2 缓存时,OpenAccess 会同步各个缓存,甚至保留事务完整性。每个修改事务都会将其逐出请求异步发送到 L2 缓存集群的所有参与者,从而确保修改的数据被过期,并防止不正确和脏读。

分布式应用程序

当今的应用程序需要在多个层之间工作,即使它们不总是连接的。通过 WCF 的面向服务体系结构、Web 应用程序、移动应用程序和异步应用程序仍然需要直接处理持久化对象。OpenAccess 通过允许您通过 OpenAccess Object Container 在断开连接模式下使用数据的一部分来支持分布式环境。此外,OpenAccess 与 Silverlight 等客户端技术集成,允许您将处理分布在多个层。

结论

如果您希望弥合对象-关系阻抗不匹配,提高您的工作效率和数据访问性能,您应该考虑 Telerik OpenAccess ORM。它提供了一个向导,支持正向和反向映射到包括 Microsoft SQL Server 在内的所有主流数据库,具有紧密的 Visual Studio 集成、LINQ 支持、透明持久化和延迟加载,可在中等信任环境中运行,并通过其获取计划和二级缓存提供完全可扩展的架构。

有关更多信息,请访问:http://www.telerik.com/products/orm.aspx

© . All rights reserved.