开发人员:开放源代码
   下载
 Oracle 数据库快捷版
 Ruby on Rails
 示例代码
 
   标签
xe, opensource, rubyonrails, 全部
 

 

Rails 上的 HR 模式


作者:Casimir Saternos

 

了解创建可使用旧模式的 Ruby on Rails Web 应用程序的技巧。

2009 年 11 月更新

Oracle 数据库有着辉煌的历史和光明的未来。它拥有最新的创新特性以及超前很多现代技术的历史。应用程序不断更替,但是组织的数据一直保留在已通过各种版本迁移的 Oracle 数据库中。由于现在存在大量的旧模式,这已经带来了技术上的挑战。许多组织面临着如何使其客户群更广泛地利用这些数据的挑战。

Ruby on Rails(或者简称“Rails”)是一个完备的应用程序框架,开发人员、DBA 和系统管理员可以使用该框架快速开发 Web 应用程序。该框架是开放源代码的,其使用渐为流行。使用该框架,您可以比使用其他框架更快速地构建和开发与数据库集成的 Web 应用程序。

现在提供的大多数 Rails 示例和教程都包括创建一个带表和列的新数据库模式,这些表和列遵守该框架要求的结构和命名惯例。这些教程还假设一个相当简单的对象关系映射 — 使用旧模式时该映射不适用。但是,该框架和 Oracle 数据库都提供一些特性,这些特性可用于创建与旧模式集成的 Web 应用程序。

本文说明创建 Oracle 演示模式 HR 的 Web 前端的技巧。本文的示例代码包括一个完整的 Ruby on Rails Web 应用程序,该应用程序基于 HR 模式开发,该模式运行于 Oracle 数据库 10g 快捷版 (XE) 上。该应用程序是在 Windows 环境下开发的,但在其他支持 rails 和 Oracle 的环境下也可运行。非 Windows 用户在执行本文所述命令时应将所有反斜线转换为正斜线。

为什么使用 Ruby on Rails?

可用于 Web 开发的平台有很多,包括 Microsoft .Net、Java 2 企业版和 PHP。此外,还有 Oracle Application Express,这是 Oracle 数据库 10g 及更高版本的一个特性(也可单独提供),人们普遍认为 Oracle Application Express 是快速开发与 Oracle 数据库集成的 Web 应用程序的理想平台。那么为什么还对一个使用相对不熟悉的语言的新平台这么感兴趣呢?

依设计,Rails 可以进行极快的 Web 应用程序开发。Rails 是通过遵守惯例而不是强制开发人员预先做出所有配置决定来做到这一点的。安装了 Ruby 和各种所需的程序包之后,您只需运行很少的几个命令并编辑一个文件 (database.yml) 就可以创建一个最小的 Web 应用程序。附带的代码生成器或代码库可应对 Web 开发中很多最常见的任务。Ruby 语言具有 Java 等语言的面向对象的功能,但是还可以用于创建简单的脚本。该语言功能全面,使您无需借助各种不同技术来完成手边的任务就可以创建应用程序。

Rails 有哪些新内容?

本文初稿撰写于 2006 年,而本文最新修订版中使用的 Rails 的版本是 Rails 2.3.4。了解这点很重要,因为该框架经过几年的时间已有了许多变化。本文随附的 Web 应用程序已经过更新,可运行于最新的 Rails 版本上。为了更新原始应用程序,我们进行了一些必要的显著更改:

  • 在 Rails 1.0 中,对于一个模型(即生成支架时使用的一个模型),我们只需要指定模型名称。而在 Rails 2.0 中,如果要通过生成器创建字段,还须指定字段名称和相应的数据类型。
  • 在 Rails 2.0 中,视图代码中使用的一些标记(如 form_tag)和 helper 方法签名(如 number_with_precision)有所更改。
  • 默认情况下,Rails 2.0 不再支持动态支架生成 (scaffolding) 功能和分页功能。为了提供分页功能,使用了一个插件以提供所需功能:
    gem sources -a http://gems.github.com
    gem install mislav-will_paginate
    

另外,开发人员还提供了专门用于 Ruby on Rails 与 Oracle 开发的开放源代码软件。随着一些工作组的参与,Oracle 对 Rails 的支持和识别也得以增强。

Rails 社区翘首以待 Rails 3.0 的发布,该版本承诺将包括另一个 Web 应用程序框架(即 Merb)的最佳特性,改变相关性的处理方式,并实现各种语法和性能改进。尽管总是在进行新的更改,但 Rails 的基本目标和功能始终是提供一个用于 Web 应用程序开发的灵活高效的框架。

Rails 因其加快了应用程序的开发速度而赢得了近乎传奇般的声誉。之所以取得这样的成功,部分是因为其要求遵守规定的编码标准和命名惯例。这些标准和惯例不仅是 Rails 框架所要求的,它们通常也是很好的编码习惯。

传统模式与 Oracle 和 Rails 集成的有关惯例包括:

  • 使用表所代表的模型的复数形式命名表。例如,“employee”模型映射到“employees”表。
  • 包含要更新数据的所有表都包含一个名为“id”的主键。
  • 如果是 Oracle 数据库,该主键使用一个序列递增,序列名称基于主键递增的表的名称。名为“employees”、包含“id”的表将按名为“employee_seq”的序列递增。

继续创建基于旧模式运行的 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 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |   243 |   201 |       7 |      35 |   5 |     3 |
| Helpers              |     0 |     0 |       0 |       0 |   0 |     0 |
| Models               |   167 |   144 |       6 |       8 |   1 |    16 |
| Libraries            |     0 |     0 |       0 |       0 |   0 |     0 |
| Functional tests     |   440 |   315 |      10 |      50 |   5 |     4 |
| Unit tests           |    50 |    35 |       5 |       5 |   1 |     5 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |   900 |   695 |      28 |      98 |   3 |     5 |
+----------------------+-------+-------+---------+---------+-----+-------+
Code LOC: 345     Test LOC: 350     Code to Test Ratio: 1:0.9

开发该应用程序需要的类不到 30 个,代码不到 350 行!

安装和运行 Web 应用程序

安装 Oracle 数据库快捷版、Ruby、Rails 和 OCI8 程序包是安装该应用程序的前提条件。由于介绍每个过程将需要一系列文章,因此我仅向您提供安装资源参考:

安装这些程序后,下载并解压缩 hr_rails.zip 文件,该文件包含与本文相关的所有代码资源。文件解压缩后,其目录结构如下。

图 1

图 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

图 2:主页

 

图 3

图 3:地区薪酬报告

 

图 4

图 4:员工清单

 

图 5
图 5:员工编辑


接下来,我们来看看应用程序的数据库和模式配置。

数据库和 HR 模式配置

安装 Rails 的服务器上必须安装了 SQL*Plus 客户端(或者 Oracle 即时客户端软件)。必须创建一个 TNSNAMES.ora 项,来引用包含要使用的 HR 模式的数据库。有必要向 Oracle 新用户说明一下:TNSNAMES.ora 文件包含用于连接 Oracle 数据库的连接信息。数据库可以位于同一计算机上也可以位于远程服务器上。连接信息包括服务器名称、数据库名称和正在使用的数据库端口。Ruby OCI8 程序包使用该信息来让 Rails 与数据库通信。OCI8 层是用 Ruby 编写的层,该层利用底层 Oracle 数据库配置(在 TNSNAMES.ora 中指定)来构建数据库连接。

要创建 Web 应用程序所需的模式对象,首先执行以下任务:

  • 创建一个新用户(此处指定为 HR_RAILS)。注意:附带的脚本会删除现有用户并重建用户。
  • 授予新用户相应的资源和权限以创建其自己的模式并访问 HR 模式。
  • 修改 HR 模式的以下方面:
    • 创建序列来填充主键。
    • 需要修改一个触发器,创建另一个触发器。job_history 表和相关约束需要这些触发器。修改的触发器在插入历史记录之前查看员工的 job_id 或 department_id 是否已经更改。如果员工已被删除,新的触发器会删除员工的工作历史记录。如果需要,可以修改触发器以对这些数据进行归档。
  • 为遵守 Rails 命名惯例的 HR_RAILS 模式创建同义词和视图。

要完成这些任务,运行 create_rails_db_user_and_privs.sql 脚本,并对提示做出相应的回答。例如:

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.
.
.
.

运行了许多命令后就创建了许多对象。在 create_db_objects.log 文件(在工作目录中创建)中查看结果。

集成数据库和应用程序数据库。面向对象的编程专家 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 模式

尽管 HR 模式基本上保持不变,但是应用程序确实需要一些调整。我添加了 Rails 用于填充表的主键的一些序列。其他应用程序可能需要访问这种序列,因此 HR 模式本身附带有这种序列。

我修改了一个 HR 模式中已经存在的触发器 (UPDATE_JOB_HISTORY),以便仅在与员工有关的工作或部门发生变化时调用 add_job_history 过程。我做这样的更改是为了将调用该触发器的情形限制在与员工的工作历史记录实际相关的字段上。我又添加了一个触发器 (DELETE_JOB_HISTORY),用于在删除员工时删除员工的工作历史记录。设置数据库约束的方式要求该触发器允许删除员工。例如,在生产应用程序中,可能归档 JOB_HISTORY,员工记录的状态可能是“非活动”。

开发过程

用于创建示例应用程序的开发过程大致如下所示:

  • 创建和更新 HR 模式。
  • 创建 HR_RAILS 用户、模式和数据库对象。
  • 通过运行以下命令创建 Rails 应用程序:
              rails hr -database oracle
    
  • 配置 database.yml 文件,以便开发连接、测试连接和生产连接通过有效的用户名和口令连接到正确的数据库。应用程序下载中附带的 database.yml 文件(可在 \hr\config 目录中找到)引用一个名为 XE 的数据库,HR_RAILS 用户使用 HR_RAILS 作为口令访问该数据库。需要将这些值修改为您创建模式时使用的值。Ruby ActiveRecord 类使用该文件建立数据库连接。--database oracle 参数不是必需的,但使用该参数会生成 database.yml 文件,该文件包括一些针对 Oracle 数据库的使用的注释。
  • 生成表的“支架”。利用这些支架(Ruby 模型、视图和控制器)可以查看、创建、更新和删除基表上的记录。这些支架是通过运行作为 Rails 应用程序一部分创建的脚本创建的。
    ruby script\generate scaffold <name>
    
  • 支架有哪些更新?

    生成支架时,现在您必须列出字段名称和参数,例如:

    ruby script\generate scaffold region name:string
    

    可用的数据类型有许多种,但只有少数几种类型是常需使用的,如 :datetime、:float、:integer 和 :string。根据所使用的版本,可能会出现一个关于生成的视图页面的 link_to 标记的错误。我们可通过将 id 明确地指定为 String 类型来解决这类问题,具体如下:

    <td><%= link_to 'Show', region %></td>
    <%= link_to 'Show', region.id.to_i.to_s %>
    

    我们还可使用前面提到的 Oracle ActiveRecord 增强适配器来解决此问题。其配置步骤包括:

    1. 下载/安装 gem
    2. 修改 environment.rb 以包括该 gem:
      config.gem 'activerecord-oracle_enhanced-adapter', :lib =>
        'active_record/connection_adapters/oracle_enhanced_adapter'
                                                
    3. 修改 database.yml 以使用 oracle_enhanced adapter 而不是 oci8:
      adapter: oracle_enhanced
      
    4. 在 config/initializers 目录下创建一个名为 oracle_advanced.rb 的文件。该文件包含您希望其包含的所有选项。要解决上述问题,指定以下内容:
      self.emulate_integers_by_column_name = true
      
  • 为该站点创建布局。布局提供您在站点的每页上看到的标题、左侧导航栏和页脚部分。
  • 修改 routes.rb 配置以便映射站点首页(这样不需要输入扩展 URL)。
  • 多次反复修改模型(如果需要附加数据)、视图(尝试使界面用户友好和实用)和控制器(获得视图数据)。这实际上是手工编码工作,旨在更改站点的样式并创建该应用程序特有的功能。

支架。在开发过程中,会生成每个表完整的模型、视图和控制器。尽管为所有正在使用的模型生成了全部支架,但最终版本并不包括全部可用功能。用户只能查看地区和国家列表。可以查看、插入、更新和删除位置、部门和员工列表。

模型。每个 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

查询。我们在代码中看不到与 INSERT、UPDATE、DELETE 和 SELECT * 查询有关的 SQL 语句,而每个 Model 类都会使用这类查询。这类代码是在后台生成的。但是,旧模式的一个共同特征就是需要复杂的 SQL 语句来检索或汇总数据。另外,Oracle 数据库还提供其他 RDBMS 中没有的强大功能。例如,它可以使用单个层次查询生成数据的树状表示。在其他数据库系统中要实现此类功能可能需要编写许多查询语句和复杂的代码。Rails 通过在 Model 类中定义一个 find_by_sql 函数,提供了一种执行 Oracle 特定 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

显示结果的页面包含 JavaScript,该 JavaScript 允许用户“下钻”层级结构。LEAD 分析函数允许访问多行而无需使用自联接。结果集中的当前行可以有效“预测”下一行中的层级,这极大地简化了用于显示层级结构的 JavaScript 代码。

图 6

图 6:Oracle 层次查询产生的员工树视图

定义了与员工模型相关的许多关系。员工属于部门,向经理汇报。在数据库级别,存在反映这些关系的外键约束。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 或 rhtml> 的布局。但是,如果您在 layouts 目录中创建一个名为 application 的布局,则没有基于控制器名称的布局的控制器将使用该布局。

视图利用了许多 Rails 类,这些类旨在将视图中显示的代码的数量减至最少。使用 number_to_currency 帮助器或 number_with_precision 帮助器将数字数据转换为货币格式。

Rails 还包括一些 JavaScript 库,这些库提供大量与 AJAX 支持和 DOM 处理相关的服务。虽然这些服务的用途并不广泛,但是可以提供点击大多数页面上的图像时使图像不显示的视觉效果。此外,还包括其他一些 JavaScript 函数,利用这些函数可实现鼠标放置按钮效果和公司层级结构下钻。

高级 Oracle 功能

示例应用程序利用 Rails 功能自动生成按主键插入、更新和删除以及从表中选择时所需的简单 SQL 语句。但是,也包括一些使用 Oracle SQL 的查询。region.rb 模型包含创建地区薪酬报告 (GROUP BY ROLLUP) 以及列出应用程序使用的视图的模式报告 (LAG OVER) 的 SQL。employee.rb 模型包括用于构建公司层级结构的查询。该查询使用 CONNECT BY 执行递归搜索以及 LEAD OVER。

总结

HR 将继续提供广受欢迎的模式来演示 SQL 功能。如该应用程序所演示的,它还作为一个例子来说明如何使用 Ruby on Rails 创建使用旧模式的 Web 应用程序。


Casimir Saternos 是 Oracle 认证 DBA、IBM 认证企业开发人员和 Sun 认证 Java 程序员,住在宾夕法尼亚州 Allentown。