2006 年 6 月发表
Oracle 数据库有着辉煌的历史和光明未来。它拥有最新的创新特性以及超前很多现代技术的历史。应用程序不断更替,但是组织的数据一直保留在已通过各种版本迁移的 Oracle 数据库中。由于现在存在大量的旧模式,这已经带来了技术上的挑战。许多组织面临着如何使其客户群更广泛地利用这些数据的挑战。
Ruby on Rails(或者简称“Rails”)是一个令人振奋的新框架,开发人员、DBA 和系统管理员可以使用该框架快速开发 Web 应用程序。该框架是开发源代码的,很快获得了广泛的认同。使用该框架,您可以比使用其他框架更快速地构建和开发与数据库集成的 Web 应用程序。
现在提供的大多数 Rails 示例和教程都包括创建一个带表和列的新数据库,这些表和列遵守该框架要求的结构和命名惯例。这些教程还假设一个相当简单的对象关系映射 — 使用旧模式时该映射不适用。但是,该框架和 Oracle 数据库都提供一些特性,这些特性可用于创建与旧模式集成的 Web 应用程序。
本文说明创建 Oracle 演示模式 HR 的 Web 前端的技巧。本文供下载的代码包括一个完整的 Ruby on Rails Web 应用程序,该应用程序在 HR 模式上开发,该模式在 Oracle Database 10g 快捷版 (XE) 上运行。
下载的应用程序可在 Windows 或 Linux 上运行。Linux 用户在执行本文说明的命令时应将所有反斜线转换为正斜线。
可用于 Web 开发的平台有很多,包括 Microsoft .Net、Java 2 企业版和 PHP。此外,大家广泛认为 Oracle 数据库 10g 的特性(也可以单独提供)Oracle Application Express(以前的 HTML DB)是快速开发与 Oracle 数据库集成的 Web 应用程序的理想平台。那么为什么还对一个使用相对不熟悉的语言的新平台这么感兴趣呢?
依设计,Rails 可以进行极快的 Web 应用程序开发。Rails 是通过遵守惯例而不是强制开发人员预先做出所有配置决定来做到这一点的。安装了 Ruby 和各种所需的程序包之后,您只需运行不到六个命令和编辑一个文件 (database.yml) 就可以创建一个最小的 Web 应用程序。附带的代码生成器或代码库可应对 Web 开发中很多最常见的任务。Ruby 语言具有 Java 等语言的以对象为导向的功能,但是还可以用于创建简单的脚本。该语言的功能范围使您无需借助各种不同技术来完成手边的任务就可以创建应用程序。
Rails 因其加速了应用程序的开发而几乎誉满天下。之所以取得这样的成功,是因为遵守了编码标准和命名惯例。除了 Rails 框架要求之外,这些标准和惯例通常也是很好的编码做法。
传统模式与 Oracle 和 Rails 集成的有关惯例包括:继续创建在旧模式下运行的 Rails 应用程序的方法有很多。一些技巧涉及使用 Rails 特性,还有一些技巧涉及修改底层数据库对象。演示 Web 应用程序并不尝试说明继续进行的每个可能方法,而是尝试利用以这样的方式利用 Oracle 和 Rails 的强大功能,提供一个清晰、开发速度快、容易维护的合理解决方案。
Rails 社区将 Ruby on Rails 框架的特征归结为“教条化”。这可以看作是肯定性的描述,表明该软件的设计加强了对标准和惯例的遵守。但是,这样的目标也引出了这样的担忧 — 该框架将限制或约束应用程序开发。我们的应用程序包括实现表明框架灵活性的一些要求。
Ruby on Rails 框架提供在新的或旧的数据库模式上快速构建 Web 应用程序所需的工具。看一下 rake(一个 Ruby 构建实用程序)的输出,这可以让您感受一下创建该项目所付出的努力:
C:\hr_rails\hr>rake stats (in E:/ruby_apps/hr_rails/hr) +----------------------+-------+-------+---------+---------+-----+-------+ | Name | Lines | LOC | Classes | Methods | M/C | LOC/M | +----------------------+-------+-------+---------+---------+-----+-------+ | Helpers | 15 | 14 | 0 | 0 | 0 | 0 | | Controllers | 243 | 193 | 7 | 35 | 5 | 3 | | Components | 0 | 0 | 0 | 0 | 0 | 0 | | Functional tests | 440 | 315 | 10 | 50 | 5 | 4 | | Models | 177 | 149 | 6 | 5 | 0 | 27 | | Unit tests | 50 | 35 | 5 | 5 | 1 | 5 | | Libraries | 0 | 0 | 0 | 0 | 0 | 0 | +----------------------+-------+-------+---------+---------+-----+-------+ | Total | 925 | 706 | 28 | 95 | 3 | 5 | +----------------------+-------+-------+---------+---------+-----+-------+ Code LOC:356 Test LOC:350 Code to Test Ratio: 1:1.0
开发该应用程序需要的类不到 30 个,代码不到 400 行!
安装 Oracle 数据库 XE、Ruby、Rails 和 OCI8 程序包是安装该应用程序的前提条件。由于介绍每个过程将需要一系列文章,因此我仅向您提供安装资源参考:
图 1:目录结构 |
该目录结构没有什么特别的,它是所有 Rails 应用程序的标准结构。应用程序的名称(本示例为 hr)显示为根目录。app 目录中的 models、views 和 controllers 目录包含大量的应用程序代码。config 目录包含 database.yml 文件(用于配置数据库连接)和 routes.rb 文件(用于定义充当应用程序索引的页面)。public 文件夹包含保存 Web 资源的目录,包括脚本、图像和样式表。尽管本文没有讨论,但 test 目录包含与对应用程序进行单元测试相关的资源。
安装了软件、创建了模式并配置了 Rails 应用程序后,就可以运行该应用程序了。从 hr 目录,运行:
ruby script\server该命令启动 WEBBrick(随安装程序附带的 Ruby Web 服务器)。在 Web 浏览器中,输入将在端口 3000 运行的 Web 应用程序的 URL,例如 http://localhost:3000。
下面列出了在应用程序中显示的页面的示例:
图 2:主页
|
图 3:地区赔偿报告
|
图 4:员工列表
|
图 5:员工编辑 |
接下来,我们来看看应用程序的数据库和模式配置。
安装 Rails 的服务器上必须安装了 SQL*Plus 客户端(或者 Oracle 即时客户端软件)。必须创建一个 TNSNAMES.ora 项,来引用包含要使用的 HR 模式的数据库。有必要向 Oracle 新用户说明一下:TNSNAMES.ora 文件包含连接 Oracle 数据库的连接信息。数据库可以位于同一计算机上也可以位于远程服务器上。连接信息包括服务器名称、数据库名称和正在使用的数据库端口。Ruby OCI8 程序包使用该信息来让 Rails 与数据库通信。OCI8 层是用 Ruby 编写的层,该层利用底层 Oracle 数据库配置(在 TNSNAMES.ora 中指定)来构建数据库连接。
要创建 Web 应用程序所需的模式对象,首先执行下列任务:
C:\hr_rails\hr>sqlplus /nolog SQL*Plus:Release 10.1.0.2.0 - Production on Wed Mar 8 12:31:01 2006 Copyright (c) 1982, 2004, Oracle.All rights reserved. SQL> @create_rails_db_user_and_privs Enter the DBA user: system Enter the DBA password: notmanager Enter the Database Name ( Oracle SID): xe Enter the (new) rails user (user will be DROPPED and created): hr_rails Enter the rails user's password: hr_rails Enter the HR user (to grant the rails user privs): hr Enter the HR user's password: hr Connected. . . .
集成数据库和应用程序数据库。以对象为导向的编程专家 Martin Fowler 对应用程序 和集成 数据库做了区分,该区分十分有用。一般来说,应用程序数据库是由单个应用程序使用的数据库,而集成数据库则被大量应用程序访问和使用。Rails 开发以使用应用程序数据库开发应用程序为导向。当前的项目显示了一种结合使用集成数据库和 Rails 的方法。
在该项目中,并没有直接使用 HR 用户访问该模式,而是创建了一个新用户(本文中命名为 hr_rails)。为 Rails 应用程序专门指定一个用户能够使 DBA 清晰地分辩访问所讨论数据库模式的应用程序。此外,Rails 鼓励使用符合标准命名惯例的表和列名称。新模式将包含与底层 HR 表相对应的视图。这些视图自然可更新而不只是能查询,因此也可用于所有插入、更新和删除操作。实际上,该方法是要努力创建一种在某种程度上独立于集成数据库模式的应用程序数据库模式(使用 Fowler 的术语)。
使用 Oracle 的“自然可更新”视图可提供一个有用的界面来利用 ActiveRecord 的对象关系功能。此外,利用这些视图还以一种最适于 Rails 访问的形式对表结构和数据进行格式设置。这些视图还可以隔离 Rails 应用程序,从而增强对访问 HR 模式的 Rails 数据库用户和其他应用程序用户的审计可见性。Oracle SQL 的所有功能(包括 Oracle 对语言和功能扩展)都可通过 find_by_sql 来加以利用。
对于应用程序内的引用,在“关于该站点”页面的一个图像上可以找到实体关系图的一部分。还有一个从该页面(以及从主页)指向另一个页面(该页面查询 Oracle 数据字典以显示视图名称以及列名称和类型)的链接。
尽管 HR 模式基本上保持不变,但是应用程序确实需要一些调整。我添加了 Rails 用于填充表的主键的序列。其他应用程序可能需要访问这种序列,因此 HR 模式本身附带有这种序列。
我修改了一个 HR 模式中已经存在的触发器 (UPDATE_JOB_HISTORY),以便仅在与员工有关的工作或部门发生变化时调用 add_job_history 过程。我做这样的更改是为了将调用该触发器的情形限制在与员工的工作历史记录实际相关的字段上。我又添加了一个触发器 (DELETE_JOB_HISTORY),用来在删除员工时删除员工的工作历史记录。设置数据库约束的方式要求该触发器允许删除员工。例如,在生产应用程序中,可能归档 JOB_HISTORY,员工记录的状态可能是“非活动”。
用于创建示例应用程序的开发过程大致如下所示:
rails hr
ruby generate\script scaffold <name>
结构。在开发过程中,会生成每个表的完整的模型、视图和控制器。尽管为所有正在使用的模型生成了全部结构,但是最终版本并不包括全部可用功能。用户只能查看地区和国家列表。可以查看、插入、更新和删除位置、部门和员工列表。
模型。每个 Model 类都有类似的特性。下面显示了代表示例。您可以参考代码查看其他详细信息。
输入域验证。Rails 框架努力将验证代码集中在 Model 类中。这种方法有很多好处,最值得一提的是使用 Model 类的所有插入和更新处理都将包括定义的约束,并在验证规则失败时向用户返回一致的消息。但是,该数据库自身还可以并应该用于约束数据。这样就可以限制任何 应用程序或 SQL 语句输入表中的数据。此外,可以使用视图层按照其设计强制执行验证。设计较好的 GUI 可以防止用户选择无效的选项。设置了长度的文本域可防止字符串超过允许的最大长度。下拉框可防止用户在需要包含外键输入的域中输入不符合要求的内容。
员工模型验证确保包括必填域;字符串数据域不超过最大长度;电子邮件唯一;工资是数字。(视图通过强制用户从下拉框中选择数据隐式验证部门和经理)。validates_presence_of :email, :hire_date, :last_name, :first_name, :phone_number, :salary validates_uniqueness_of :email validates_length_of :first_name, :maximum => 20 validates_length_of :last_name, :maximum => 25 validates_length_of :email, :maximum => 25 validates_length_of :phone_number, :maximum => 20 validates_numericality_of :salary, :only_integer => true此外,员工模型中有自定义验证代码,可确保工资和佣金都设置在有效范围内:
def validate if salary != nil if salary < 1 errors.add(:salary, "must be positive.") end if salary > 999999 errors.add(:salary,"must be less than 999999") end end if commission !=nil unless commission >= 0 && commission < 1 errors.add(:commission, "must be greater than 0 and less than 1.") end end end
查询。在与每个 Model 类使用的 INSERT、UPDATE、DELETE 和 SELECT * 查询相关的代码中看不到 SQL 语句。但是,旧模式的一个共同特征就是需要复杂的 SQL 语句来检索或汇总数据。Rails 通过在 Model 类中定义一个 find_by_sql 函数,提供了一种执行复杂的 SQL 语句的方法。
构建公司层级结构的查询如下所示:SELECT tree.*, LEAD (hierarchy_level) OVER (ORDER BY seq) next_level FROM ( SELECT CONNECT_BY_ROOT last_name top_node_name, (last_name || ', ' || first_name ||' ('||job_title||')') employee_name, emp.id employee_id, SYS_CONNECT_BY_PATH (last_name, '->') node_path, LEVEL hierarchy_level, ROWNUM seq FROM( SELECT e.*, j.job_title FROM employees e, jobs j WHERE e.job_id = j.id ) emp START WITH emp.job_title= 'President' CONNECT BY PRIOR emp.id = manager_id ORDER SIBLINGS BY emp.id ) tree ORDER BY seq
以上代码是使用 Oracle SQL 扩展创建的示例层次查询。该代码用于返回构成公司层级结构示例的结构中的数据。显示结果的页面包含 JavaScript,该 JavaScript 允许用户“下钻”层级结构。LEAD 分析函数允许访问多行而无需使用自联接。结果集中的当前行可以有效“预测”下一行中的层级,这极大地简化了用于显示层级结构的 JavaScript 代码。
图 6 |
定义了与员工模型相关的许多关系。员工属于部门,向经理汇报。在数据库级别,存在反映这些关系的外键约束。Rails 框架还将这些关系为指定一对多。经理本身是员工表中的一个字段。
belongs_to :department belongs_to :manager, :class_name => "Employee", :foreign_key => "manager_id" belongs_to :job has_many :department has_many :managed_employee, :class_name => "Employee", :foreign_key => "manager_id"
这些关系设置了大量方便方法,使得程序员通过模型对象轻松访问相关数据。
控制器。对生成的控制器对象的修改相对较少。主控制器为主视图和活动视图添加了方法。员工视图包括几个对用于填充经理和部门下拉框的模型的附加调用。其余代码只是自动生成的结构的一部分。
修改了 config\routes.rb 文件,以便 http://localhost:3000 映射到应用程序的主页。
视图。站点中的所有页面都有公用的标题、左侧导航栏和页脚。这些都被整合到一个布局 文件中。默认情况下,Rails 查找 /app/views/layout 目录中一个名为 <controller_name>_layout.<xml or rhtml> 的布局。但是,如果您在 layouts 目录中创建一个名为 application 的布局,则没有基于控制器名称的布局的控制器将使用该布局。
视图利用了许多 Rails 类,这些类旨在将视图中显示的代码的数量减至最少。使用 number_to_currency 帮助器或 number_with_precision 帮助器将数字数据转换为货币格式。
Rails 还包括一些 JavaScript 库,这些库提供大量与 AJAX 支持和 DOM 处理相关的服务。虽然这些服务的用途并不广泛,但是可以提供点击大多数页面上的图像时使图像不显示的视觉效果。此外,还包括其他一些 JavaScript 函数,利用这些函数可实现鼠标放置按钮效果和公司层级结构下钻。
示例应用程序利用 Rails 功能自动生成主键插入、更新和删除以及从表中选择所需的简单 SQL 语句。但是,也包括一些使用 Oracle SQL 的查询。region.rb 模型包含创建地区赔偿报告 (GROUP BY ROLLUP) 以及列出应用程序使用的视图的模式报告 (LAG OVER) 的 SQL。employee.rb 模型包括用于构建公司层级结构的查询。该查询使用 CONNECT BY 执行递归搜索以及 LEAD OVER。
HR 将继续提供广受欢迎的模式来演示 SQL 功能。如该应用程序所演示的,它还是说明如何使用 Ruby on Rails 创建利用旧模式的 Web 应用程序的示例。