Ruby 语言的出现并与强大的 Rails 框架结合,为 Web 解决方案的开发带来了巨大的机遇。随着 IBM_DB 适配器和驱动程序的引入,Rails 应用程序现在可以无缝地与 IBM 数据服务器进行交互。本文是 DB2 with Ruby on Rails 系列的第一篇文章。文中介绍了 Starter Toolkit for DB2 on Rails,谈到了安装 IBM_DB 驱动程序的多种方法以及使用 DB2 的 Rails 迁移。
Ruby on Rails 发布于 2004 年,如今已快速成为 Web 应用程序开发中最流行的框架之一。这个开放源代码项目又名 Rails 或 RoR,它采用 Model-View-Controller (MVC) 架构和 Ruby 面向对象脚本编制语言,并遵循一些简单的原则,例如 “约定优于配置(convention over configuration)” 和 “不要重复自己(don't repeat yourself)”。因此,开发人员可以更快、更容易地构建应用程序,减少冗余的代码和配置文件,同时又能灵活地创建定制的扩展,以满足应用程序需求。通过对数据库持久性的支持,可以使用数据库服务器和内置的 WEBrick Web 服务器快速开发使用这种框架的 Web 应用程序。
随着 RubyForge 社区门户上开始提供 IBM_DB Rails 适配器和 Ruby 驱动程序,Ruby on Rails 框架经过官方测试,并且在所有 DB2 数据服务器上受支持。对于你们当中某些人来说这也许是个惊喜,社区还有一个免费版本的名为 DB2 Express-C 的 DB2 9 数据库可以使用。DB2 Express-C 数据服务器的开发、部署和分发是免费的,没有大小、时间或用户方面的限制,而且还包括了 Starter Toolkit for DB2 on Rails。这个包可以帮助开发人员在使用 DB2 的 RoR 环境中快速、轻松地设置和构建 Web 应用程序。
虽然在 Rails 框架中可以配置的数据存储有很多种,但 DB2 数据服务器可以带来一些独特的、突出的优点。由于 DB2 9 中引入了 pureXML™,使用 IBM 数据服务器的首要优点是,IBM_DB 适配器和驱动程序支持本地 XML 数据类型。
目前有两种方法可以设置使用 DB2 的 Rails 开发环境。如果您对 DB2 环境不熟悉,那么可以借助 Starter Toolkit for DB2,这是开始进行 DB2 on Rails 应用程序开发的最方便的方式。Starter Toolkit version 2.1 可以从 下载,其中还有一个稍旧版本的 IBM_DB 适配器的安装程序。这个版本不支持 i5 和 zOS 平台上的 DB2,但是更新工作正在进行,很快就会有新版本可供下载。
在 IBM_DB 适配器和驱动程序 gem 及插件发布之后,如果要在一个已有的 DB2 环境中开发 Rails 应用程序,那么可以使用 “manual” 选项,这个选项使用起来同样也很轻松。请参阅 获得最新版本(从 4 月 30 日起,Production/Stable release 0.6.0 已经可用)。
首先下载和配置 DB2 Express-C 数据服务器和 Rails 运行时环境。最新版本的 IBM_DB 适配器和驱动程序要求使用 DB2 9,FixPack 2 或 DB2 8,FixPack 15。为了下载和配置 DB2 Express-C 数据服务器和 Rails 运行时环境:
安装 Rails gem 及其依赖项:
gem install rails --include-dependencies |
为了帮助理解 Ruby gem 安装与 Rails plugin 安装之间的区别,下面简要描述一下运行时环境。
RubyGems 是用于 Ruby 运行时环境中的库和应用程序的标准打包和安装框架。对于每个 bundle,在一个中央存储库中会发布并存储一个遵从打包格式的名为 gem 的文件,以允许同时部署同一个库或应用程序的多个不同的版本。与 Linux 发布打包管理和 bundle(.rpm、.deb)类似,通过 gem 终端用户实用程序也可以查询、安装、卸载和操作这些 gem。gem 实用程序可以无缝地查询远程 RubyForge 中央存储库,并且可以查找和安装任何已有的能使 Rails 开发人员更轻松的实用程序。安装了 IBM_DB gem 之后,马上便可以在 Ruby 运行时环境中通过以下命令从任何脚本(和应用程序)中访问其功能:
作为一个 ActiveRecord 适配器,在 Rails 框架中使用它之前,IBM_DB gem 需要由抽象适配器注册到它的 RAILS_CONNECTION_ADAPTERS (active_record.rb) 列表中。注册之后,会加载 IBM_DB gem 和它的依赖项(ibm_db Ruby driver、IBM Driver for ODBC 和 CLI)。这样便使得 Ruby 环境中的任何应用程序,包括 Rails,可以与 IBM 数据服务器交互。正如 IBM_DB gem README 文件(见本文 参考资料 小节)中描述的那样,每过几个简单的步骤,就要启用 Ruby 运行时,以访问 IBM 数据服务器。
将 IBM_DB 适配器和驱动程序安装为 Ruby Gem
发出 gem 命令,安装 IBM_DB 适配器和驱动程序:
D:\>gem install ibm_db |
将 ‘ibm_db’ 注册到 Rails 框架中的连接适配器列表中。
将 ibm_db 手动添加到 gems\1.8\gems\activerecord-1.15.3\lib\active_record.rb:
RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite ... ibm_db ) |
Rails 插件为该框架自身提供了扩展机制。通过这种机制,Rails 可以在一个特定的 Rail 应用程序范围内扩展其功能。因此,当 Ruby 运行时环境中没有部署 IBM_DB gem 时,这提供了另一种访问 IBM 数据服务器的方法。虽然 Rails 插件没有提供特定于 Ruby gem 的版本管理,但是它们提供了一种有用的初始化机制,该机制允许 IBM_DB 插件在其初始化期间自己插入到 Rails 注册表中。因此,在应用程序中以插件形式安装 IBM_DB 之后,无需任何手动步骤,就可以让 Rails 框架加载它。正如 IBM_DB 插件 README 文件和 RubyForge 上的 中描述的那样,只需注册插件源,并运行 Rails 应用程序安装脚本,就可以启用 Rails 应用程序,以便进行 IBM 数据服务器访问。虽然 RubyForge 可以启用到 Subversion (SVN) 库的 HTTP 访问(目前只支持 SVN 协议),在 Windows 上安装 Rails 插件时,仍然需要一个 SVN 客户机。在大多数 Linux® 和 UNIX® 发行版上,SVN 客户机是默认提供的,这使得 IBM_DB 插件安装具有很好的无缝性。
IBM_DB 适配器(ibm_db_adapter.rb)对 ibm_db 驱动程序存在直接依赖,它利用 IBM Driver for Open Database Connectivity (ODBC) and Call Level Interface (CLI) 连接到 IBM 数据服务器。 IBM CLI 是 IBM 数据服务器的可调用 SQL 接口,它遵从 ODBC。
对于 IBM_DB 适配器和驱动程序还有一些额外的注意事项。
IBM Driver for ODBC 和 CLI 的安装必需满足 IBM_DB 需求。
可以通过完全安装 DB2 数据库获得 IBM Driver for ODBC 和 CLI,也可以从 “” 中单独获得这两个组件。
可以使用 CLI 关键词在任何 Ruby 应用程序之外修改驱动程序行为。
可以使用一些 CLI 关键词在 Rails 应用程序之外修改某些事务行为。例如,可以使用它们来设置当前模式或者修改一些事务元素,例如关闭自动提交行为。关于 CLI 关键词的细节可以参阅以下 DB2 Info Center 文档:
诊断信息的收集需要 CLI driver 跟踪实用程序。
由于通过 IBM_DB driver 的所有请求都是使用由 IBM Driver for ODBC 和 CLI 提供的 API 实现的,CLI 跟踪是识别使用 IBM_DB 适配器和驱动程序的应用程序中的问题的重要机制。
CLI 跟踪捕捉应用程序向 IBM driver for ODBC 和 CLI 发出的所有 API 调用(包括所有输入参数),并捕捉驱动程序返回给应用程序的所有值。它是设计用来捕捉应用程序如何与 IBM driver for ODBC 和 CLI 交互的一种接口跟踪,并提供关于驱动程序内部工作的信息。
在一个多变的环境中,应用程序需要动态地做出调整,以解决新的需求和挑战。当应用程序开发人员更改他们的应用程序时,例如添加一个新的对象或类,就需要修改底层持久性,以确保数据库与应用程序同步。更改数据库模式的传统方法是生成新的 SQL 脚本。但是,通过 SQL 脚本难于按版本依次保存应用程序和数据库。而且,数据库开发人员很少构建 SQL 脚本来逆转数据库模式中做出的与应用程序更改相关联的更改。在大多数开发环境中,更改是通过删除所有数据库对象,并使用之前版本的 SQL(DDL)脚本重新创建它们来逆转的。
对于很多 Web 开发人员来说,在 Rails 上的主要发现是其内置的对通过迁移来变更模式的支持。虽然数据库开发人员肯定更倾向于通过 DDL 或数据操作语言(DML)使用 SQL,但大多数应用程序开发人员更乐意坚持使用他们的工具:Ruby 语言和它的库。这正是 Rails 通过迁移提供的东西:一个简单而有效的基础设施,它利用 ActiveRecord 抽象按版本依次创建和修改数据库对象,例如表和列。通过 Rails 迁移完成模式变更这一主要的数据库管理任务。Rails 框架简化了开发,但是相关的工具(rake 任务)在驱动数据库更改和使应用程序代码与表结构同步方面也非常有效。
Ruby on Rails 迁移可以解决前面描述的涉及数据库和数据结构更改的一些问题。现在,让我们看看 Rails 迁移如何为数据库模式变更提供帮助。
我们来考察一个 Rails 迁移的例子,该例子使用 IBM_DB 适配器,并尝试一些与 Rails 应用程序变更相关的数据库对象更改。 但是,首先需要确保像前面描述的那样安装和配置了 DB2 on Rails 开发环境。
我们的例子将尝试构建一个 Team Room,这是一个托管应用程序,它使注册的成员可以共享文本或二进制格式的各种文档,包括图像、可执行文件和任何其他媒体。另外还包括被共享的 XML 文档,因为通过 pureXML 数据类型很容易将它们本地存储在 DB2 9 中。这个例子还将发现利用 XML 文档内置层次结构的一些好方法。
首先,创建一个名为 “teamroom” 的 Rails 项目:
D:\rails>rails teamroom create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments create components create db |
Rails 框架自动生成该项目的目录结构。从这里开始,我们假设是在 D:\rails\teamroom 目录中,此后提到的所有路径都是 Team Room 项目目录下的相对路径。
如果已经有一个 DB2 数据库,那么可以忽略这一步,直接进入步骤 3,开始配置数据库连接。
预计可能需要在本地将 XML 存储在 DB2 中,因此让我们使用 UTF-8 编码集创建 XMLDB 数据库。这里必须使用 UTF-8 编码集,以便在 DB2 表中定义 XML 列。
在 DB2 Command Line Processor 中,运行以下命令:
db2 create db xmldb using codeset utf-8 territory us |
现在编辑 D:\rails\teamroom\config\database.yml 文件,以建立到 DB2 9 XMLDB 数据库的连接。
# IBM DB2 Database configuration file # # Install the IBM DB2 driver and get assistance from: # development: adapter: ibm_db database: xmldb username: user password: secret schema: teamroom application: TeamRoom account: devuser workstation: devbox # == remote TCP/IP connection (required when no local database catalog entry available) # host: bigserver // fully qualified hostname or IP address # port: 50000 // data server TCP/IP port number |
下面的表中解释了 DB2 的每个连接属性:
连接属性 | 描述 | 是否必需 |
---|---|---|
Adapter | Ruby 适配器名称,对于 DB2 为 ‘ibm_db’ | 是 |
Database | Rails 项目所连接到的数据库 | 是 |
Username | 用于连接到 DB2 数据库的用户 ID | 是 |
Password | 指定的用户 ID 的密码 | 是 |
Schema | 命名对象的集合。模式提供了在数据库中在逻辑上组织对象的方式。在这个例子中,我们将 Rails Team Room 项目的所有数据库对象组织在 ‘teamroom’ 数据库模式之下。这样便允许多个 Rails 项目共享一个数据库 | 可以将默认模式设置为当前会话用户的授权 ID(见注 4) |
Application | 当使用 DB2 Connect 时,用于标识客户机应用程序名称的一个字符串被发送至主机数据库服务器。在 DB2 Connect 上,发出 ‘db2 list applications’ 后将显示 ‘application’ 名称,而不是 Ruby 可执行文件。 | 可选 |
Account | 当使用 DB2 Connect 时,用于标识客户机帐户的一个字符串被发送至主机数据库服务器 | 可选 |
Workstation | 当使用 DB2 Connect 时,用于标识客户机工作站名称的一个字符串被发送至主机数据库服务器 | 可选 |
Host | 数据库所在远程服务器的主机名 | 可选(见注 5) |
Port | 该参数包含数据库服务器用于等待来自远程客户机的通信的 TCP/IP 端口的名称 | 可选(见注 5) |
现在可以开始执行实际的迁移步骤了。
首先需要从一个存储区开始,使所有文档和媒体文件在这个托管应用程序的用户之间共享。因此,首先创建 DOCUMENTS 表,用于存储小组成员要共享的所有媒体文件。
我们定义以下列来描述共享文件的内容:
列名 | 数据类型 | 描述 |
---|---|---|
ID | Integer | 主键 |
Name | VARCHAR | 文档名称 |
Size | Integer | 文件大小 |
Data | BLOB | 以二进制模式存储的文件,最大 2 M |
Content_type | VARCHAR | 文档类型,包括 .doc、.ppt、.pdf、.sw、.exe、.a、.so、 .gif、.jpeg、.mov、.avi、.mpeg、.bmp 等 |
a) 为了通过迁移的方式完成这个工作,执行 ruby script\generate migration create_docs_store,以生成表 DOCUMENTS 的迁移。Rails 生成迁移,并创建 db/migrate/001_create_docs_store.rb。在 UNIX 上,可以输入 ruby script/generate migration create_docs_store。(注意正斜杠 “/” 在 Windows 和 UNIX 上都适用。)
D:\rails\teamroom>ruby script/generate migration create_docs_store create db/migrate create db/migrate/001_create_docs_store.rb |
注意,001_ create_docs_store.rb. 是在 db/migrate 中创建的,并且被编号为 ‘001’。从这一步开始,对于每个迁移步骤,db/migrate 中生成的文件依次递增编号。这表明 Rails 正在管理迁移的次序。
b) 像下面这样编辑 create_docs_store.rb:
class CreateDocsStore < ActiveRecord::Migration def self.up create_table :documents do |t| t.column :name, :string, :null => false t.column :size, :integer, :null => false t.column :content_type, :string, :null => false t.column :data, :binary, :limit => 2.megabytes end end def self.down drop_table :documents end end |
在 Rails 应用程序中,模式的更改是通过迁移发生的。对数据库的每次更改都被进行版本控制,并在包含独立于供应商的语法的 Ruby 脚本中定义。而且,每个迁移脚本提供两个类方法:up 方法和 down 方法,这使得撤销更改与做出更改一样容易。对逻辑数据库模式做出更改所需的代码都放在 self.up 方法中。而撤销更改所需的代码都放在 self.down 方法中。
c) 通过 rake db:migrate 命令运行迁移:
D:\rails\teamroom>rake db:migrate (in D:/rails/teamroom) == CreateDocsStore: migrating ================================================= -- create_table(:documents) -> 0.2010s == CreateDocsStore: migrated (0.2010s) ======================================== |
d) 现在进入到 DB2 命令提示符下,发出 db2 list tables for schema teamroom,以确认 DOCUMENTS 表已创建:
D:\>db2 list tables for schema teamroom Table/View Schema Type Creation time ------------------------------- --------------- ----- -------------------------- DOCUMENTS TEAMROOM T 2007-04-21-21.00.18.131001 SCHEMA_INFO TEAMROOM T 2007-04-21-21.00.17.740001 2 record(s) selected. |
注意,DOCUMENTS 表如预期般被创建,并且数据类型如下:
D:\>db2 describe table teamroom.documents Column Type Type name schema name Length Scale Nulls ------------------------------ --------- ------------------ -------- ----- ------ ID SYSIBM INTEGER 4 0 No NAME SYSIBM VARCHAR 255 0 No SIZE SYSIBM INTEGER 4 0 No DATA SYSIBM BLOB 2097152 0 Yes CONTENT_TYPE SYSIBM VARCHAR 255 0 No 5 record(s) selected. |
还需注意,在这个迁移期间,Rails 还创建了一个名为 SCHEMA_INFO 的表。SCHEMA_INFO 是通过 rake db:migrate 命令创建的。查询 SCHEMA_INFO 表时可以得到如下输出:
D:\>db2 select * from schema_info VERSION ----------- 1 1 record(s) selected. |
version 列中的值 ‘1’ 表明当前的迁移版本为 1(还记得之前曾运行过的 db/migrate/001_ create_docs_store.rb 吗)。
所以 rake db:migrate 实际上做以下事情:
在创建 DOCUMENTS 表之后,假设您决定存储关于每个文档的附加信息,例如操作系统平台、上传时间和最近更改时间。产生的 DOCUMENTS 表应该包含以下列(新添加的列用粗体标示)。
列名 | 数据类型 | 描述 |
---|---|---|
ID | Integer | 主键 |
Name | VARCHAR | 文档的名称 |
Size | Integer | 文件大小 |
Data | BLOB | 以二进制模式存储的文件,最大 2 M |
Content_type | VARCHAR | 文档类型,包括:.doc、.ppt、.pdf、.sw、.exe、.a、.so、.gif、.jpeg、.mov、.avi、.mpeg、.bmp 等 |
Created_at | TIMESTAMP | 文件被上传的时间(见注 9) |
Updated_at | TIMESTAMP | 文档最近更新时间戳(见注 9) |
Platform | VARCHAR | 特定于文件平台的信息 |
然后生成第二个迁移,以便将这些属性添加到 DOCUMENTS 表中:
a) 运行 ruby script/generate migration add_docs_attributes。
这样会生成 db/migrate/002_add_docs_attributes.rb 文件。
D:\rails\teamroom>ruby script/generate migration add_docs_attributes exists db/migrate create db/migrate/002_add_docs_attributes.rb |
b) 像下面这样编辑 002_add_docs_attributes.rb:
class AddDocsAttributes < ActiveRecord::Migration def self.up add_column :documents, :created_at, :timestamp add_column :documents, :updated_at, :timestamp add_column :documents, :platform, :string, :limit => 10 end def self.down remove_column :documents, :created_at remove_column :documents, :updated_at remove_column :documents, :platform. end end |
c) 同样,发出 rake db:migrate 来执行第二个迁移。
D:\rails\teamroom>rake db:migrate (in D:/rails/teamroom) == AddDocsAttributes: migrating =============================================== -- add_column(:documents, :created_at, :timestamp) -> 0.0500s -- add_column(:documents, :updated_at, :timestamp) -> 0.0100s -- add_column(:documents, :platform, :string, {:limit=>10}) -> 0.0000s == AddDocsAttributes: migrated (0.0600s) ====================================== |
组织或社区中的很多人都可能对这些文档感兴趣,所以需要有一种方式来管理那些用户和他们的访问。为此我们添加一个 USERS 表。另外还需要添加一个外键 'user_id' 到 DOCUMENTS 表中,以便知道哪个用户上传某个特定的文档。
下面是执行这些任务所需的步骤:
a) 运行 ruby script/generate migration create_users_table,这将创建 db/migrate/003_create_users_table.rb 文件
b) 像下面这样编辑 db/migrate/003_create_users_table.rb 文件:
class CreateUsersTable < ActiveRecord::Migration def self.up create_table :users do |t| t.column :usertype, :string, :limit => 5, :null => false t.column :firstname, :string, :limit => 30 t.column :lastname, :string, :limit => 30 t.column :extension, :string, :limit => 4 end add_column :documents, :user_id, :integer end def self.down drop_table :users remove_column :documents, :user_id end end |
c) 运行 rake db:migrate 以创建 USERS 表:
D:\rails\teamroom>rake db:migrate (in D:/rails/teamroom) == CreateUsersTable: migrating ================================================ -- create_table(:users) -> 0.1400s -- add_column(:documents, :user_id, :integer) -> 0.0000s == CreateUsersTable: migrated (0.1400s) ======================================= |
d) 现在可以发出 ruby script/generate scaffold document,以便为 DOCUMENTS 表生成一个 scaffold。scaffolding 通过提供列出、显示、创建、更新和销毁类的对象的标准化动作,快速地使一个 Active Record 类上线。如下面的清单 15 所示,在 scaffold 生成期间,/app/controllers 和 /app/views 中创建了一个控制器和多个视图。
D:\rails\teamroom>ruby script/generate scaffold document exists app/controllers/ exists app/helpers/ create app/views/documents create app/views/layouts/ create test/functional/ dependency model create app/models/ exists test/unit/ exists test/fixtures/ create app/models/document.rb create test/unit/document_test.rb create test/fixtures/documents.yml create app/views/documents/_form.rhtml create app/views/documents/list.rhtml create app/views/documents/show.rhtml create app/views/documents/new.rhtml create app/views/documents/edit.rhtml create app/controllers/documents_controller.rb create test/functional/documents_controller_test.rb create app/helpers/documents_helper.rb create app/views/layouts/documents.rhtml create public/stylesheets/scaffold.css |
至此,可以看看 scaffold 可以做哪些事情。在命令提示符下,输入 ruby script/server 以启动用于 Rails 的内置 WEBrick Web 服务器:
D:\rails\teamroom>ruby script/server => Booting WEBrick... => Rails application started on => Ctrl-C to shutdown server; call with --help for options [2007-04-26 16:54:57] INFO WEBrick 1.3.1 [2007-04-26 16:54:57] INFO ruby 1.8.5 (2006-12-25) [i386-mswin32] [2007-04-26 16:54:57] INFO WEBrick::HTTPServer#start: pid=444 port=3000 |
注意端口号。默认端口号为 3000,但是如果在系统上端口 3000 已经被占用,那么端口号可能有所不同。打开一个 Web 浏览器,进入 ,您将注意到一条欢迎登录消息。浏览至 documents,您将看到:
e) 编辑生成的 /app/models/document.rb 文件,使之如下所示:
class Document < ActiveRecord::Base belongs_to :user end |
belongs_to 表达了 DOCUMENTS 与 USERS 表之间一对一的外键关系。这种关联表明,每个文档只能与一个用户关联(即只能属于一个用户)。如果 DOCUMENTS 表有一个 user_id 外键列,则文档模型为 belongs_to :user。
我们在 /app/models/document.rb 中添加附加的代码,以便可以真正上传文档,并将文件存储在 DB2 9 数据库中。请参阅下载小节,以查看代码实现。在将很多文档添加到 Team Room 之后,在浏览器中可以看到如下所示的界面:
f) 类似地,发出 ruby script/generate scaffold user,以便为 Users 表生成一个 scaffold。然后编辑 /app/models/user.rb,使之如下所示:
class User < ActiveRecord::Base has_many :document end |
现在,您已经详细理解了前面几个迁移步骤,接下来我们执行剩下的迁移,以创建所需的其他表和关联。
如果有一种方式来对社区所贡献的大量文档进行分类,以便添加基于内容主题的分类学,那么显然很有帮助。SUBJECTS 表将被创建,以帮助对文档进行分类。一个主题可以包含很多文档,但是每个文档只能属于一个主题。为了演示 SUBJECTS 与 DOCUMENTS 之间的这种一对多的关系,需要将一个 Subject ID 外键添加到 DOCUMENTS 中。
下面是执行这些任务所需的步骤:
a) 运行 ruby script/generate migration create_subjects_table,这将创建 db/migrate/004_create_subjects_table.rb 文件。
b) 像下面这样编辑 db/migrate/004_create_subjects_table.rb:
class CreateSubjectsTable < ActiveRecord::Migration def self.up create_table :subjects do |t| t.column :name, :string, :limit => 20 t.column :size, :integer t.column :description, :text t.column :tag, :string, :limit => 10 end add_column :documents, :subject_id, :integer end def self.down drop_table :subjects remove_column :documents, :subject_id end end |
c) 运行 rake db:migrate,以创建 SUBJECTS 表并添加 subject_id 外键。
d) 运行 ruby script/generate scaffold subject,为 SUBJECTS 表生成 scaffold。
e) 添加 has_many :document 关联到新生成的 /app/models/subject.rb 文件中。
class Subject < ActiveRecord::Base has_many :document end |
f) 再添加一个关联 belongs_to :subject 到步骤 3(d) 中生成的 /app/models/document.rb 文件中。
class Document < ActiveRecord::Base
belongs_to :user
belongs_to :subject
<... code to assist with document uploading ...>
<... ...>
end
end
|
当一个新文档被上传到一个特定类别时,如果有一种方式来通知用户,那么会怎样呢?如果可以在一个 SUBSCRIPTIONS 表中收集那些对通知的请求,就很容易实现这一点。这里还需要考虑用户、主题和订阅之间的一些关系。
我们首先来描述用例场景:用户 Anthony 想在关于某些主题:编程、Linux 和钓鱼的新文档被上传时收到通知。他将浏览至一个显示目前已创建的所有主题的页面,然后勾选与上述主题相关的复选框。然后,每当关于这三个主题中任何一个主题的新文档被上传时,应用程序将生成一个通知消息,该消息通过电子邮件被发送到订阅了这些主题的用户。整个场景可以表达为以下一组关系:
通过与前面类似的迁移步骤,可以达到这些数据库设计目标:
a) 运行 ruby script/generate migration create_subscriptions_table 将创建 db/migrate/005_create_subscriptions_table.rb 文件。
b) 编辑 db/migrate/005_create_subscriptions_table.rb:
class CreateSubscriptionsTable < ActiveRecord::Migration def self.up create_table :subscriptions do |t| t.column :name, :string, :limit => 20 t.column :description, :text t.column :user_id, :integer end add_column :subjects, :subscription_id, :integer end def self.down drop_table :subscriptions remove_column :subjects, :subscription_id end end |
c) 运行 rake db:migrate 以创建 SUBSCRIPTIONS 表并将外键列添加到 SUBJECTS 中。
d) 运行 ruby script/generate scaffold subscription 为 SUBSCRIPTIONS 生成一个 scaffold。
e) 将 has_many :subject 关联添加到步骤 4 d 生成的 /app/models/subscription.rb 文件中。
f) 将相应的关联 belongs_to :subscription 添加到新生成的 /app/models/subject.rb 文件中。
g) 将 has_one :subscription 关联添加到步骤 3 f 生成的 /app/models/user.rb 文件中。
h) 将 belongs_to :user 关联添加到新生成的 /app/models/subscription.rb 文件中。
将关于 Team Room 更新的消息通知给用户的一种方法是通过电子邮件。为此,需要添加每个用户的电子邮箱,因此通过另一个迁移将一个 email 列添加到 USERS 表中:
a) 运行 ruby script/generate migration addEmailToUser。这将生成 db/migrate/006_add_email_to_user.rb 文件。
b) 编辑 006_add_email_to_user.rb。
class AddEmailToUser < ActiveRecord::Migration def self.up add_column :users, :email, :string, :limit => 30 end def self.down remove_column :users, :email end end |
c) 运行 rake db:migrate,将 email 列添加到 USERS 表中。
下面的图演示了步骤 1 到步骤 6 所执行的操作。
在如今的企业环境中,越来越频繁地需要以 XML 格式存储文档,而这种上乘的结构化文档并不总是被大大有别于一些无定形的二进制对象来对待。在这个应用程序中,我们大胆尝试一些新鲜事物,以 DOCUMENTS 表中已经定义的 BLOB 之外的数据类型存储 XML 文档。
如果使用 DB2 9 数据服务器,并利用本地 XML 数据持久性,则可以按分层格式存储格式良好的 XML 文档。在 DB2 9 中,XML 列被声明为 XML 数据类型,这正是我们将在新的迁移中要使用的。
a) 运行 ruby script/generate migration add_xml_doc_column。
这将生成 db/migrate/007_add_xml_doc_column.rb。
b) 编辑 007_add_xml_doc_column.rb:
class AddXmlDocColumn < ActiveRecord::Migration def self.up add_column :documents, :xmldata, :xml end # Currently, a column that is part of a table containing an XML column # cannot be dropped. To remove the column, the table must be dropped # and recreated without the previous XML column. def self.down drop_table :documents create_table :documents do |t| t.column :name, :string, :null => false t.column :size, :integer, :null => false t.column :data, :binary, :limit => 2.megabytes t.column :content_type, :string, :null => false t.column :created_at, :timestamp t.column :updated_at, :timestamp t.column :platform, :string, :limit => 10 t.column :user_id, :integer t.column :subject_id, :integer end end end |
c) 运行 rake db:migrate,将新的列添加到 DOCUMENTS 表中。
虽然 add_column 看上去有点熟悉,但是您可能会惊奇地发现需要删除整个表,然后重新创建表,而不是使用 remove_column。考虑到 XML 作为本地数据类型给表带来的重大变化,这应该不奇怪。在 XML 列移除被支持之前,用户可能感觉到这一点非常不方便,不过在本系列接下来的部分中,我们将给出另一个数据库模式设计,以及本地 XML 数据类型支持所带来的重要优点。但是首先,我们还是体验一下之前考虑的 “无定形” blob 数据。
d) 分析收集并格式化为 XML 文档的市场数据。
我们试着从 Team Room 中发布的一个文档中提取客户的邮政编码信息,以便确定收集到的市场数据中提供的所有多伦多地区的客户。当浏览至 时,使用文档 scaffold,并上传以示例代码提供的 teamroom/test/fixtures/CAN-Central.xml 文档。然后,将以下动作添加到 documents_controller.rb 中:
def zips @id = params[:id] @xmldata = Document.find_by_sql "select xmlquery(\ ' |
对于这个应用程序当前的阶段,这个复杂的 SQL 和嵌入式 XQuery 的结果只能打印在服务器控制台中,后面紧接之前上传的 CAN-Central.xml 文档的 zips 链接。这个结果将包含与 CAN-Central.xml 文档中存在的多伦多客户相关的邮政编码地区列表。乍看之下,嵌入到 SQL 语句中的 XQuery 可能有些复杂,但是在大多数情况下通过 XPath 查询表达式可以简化之:
Document.find_by_sql "select xmlquery(' |
在本系列接下来的部分中,我们将发现更好的方法来通过 SQL 嵌入式 XQuery 和 XPath 表达式简化 Rails 应用程序交互。暂时可以保证的是,您不再需要为了查看一个大型 XML 文档的一个小片段而检索其所有内容。这个工作可以在 DB2 本地 XML 数据存储中完成,同时还可以利用 DB2 引擎优化器。
图 4 显示了在步骤 7 结束时 Team Room 数据库对象应有的样子:
最后,我们已经完成了任务。但是,如果现在要撤消所有更改,那么该怎么办呢?这很简单,只需运行 rake db:migrate VERSION=number ,其中 number 表示要回滚到的版本。例如,如果要销毁我们在 XMLDB 数据库中为 Rails Team Room 项目创建的表,并撤消到目前为止做出的所有更改,那么可以发出 rake db:migrate VERSION=0。Rails 按顺序逆转每个迁移步骤,首先回滚最近的迁移,最终将数据库回滚到版本 0。
D:\rails\teamroom>rake db:migrate VERSION=0 (in D:/rails/teamroom) == AddXmlDocColumn: reverting ================================================= -- drop_table(:documents) -> 0.0150s -- create_table(:documents) -> 0.1880s == AddXmlDocColumn: reverted (0.2030s) ======================================== == AddEmailToUser: reverting ================================================== -- remove_column(:users, :email) -> 0.1250s == AddEmailToUser: reverted (0.1250s) ========================================= == CreateSubscriptionsTable: reverting ======================================== -- drop_table(:subscriptions) -> 0.0000s -- remove_column(:subjects, :subscription_id) -> 0.1560s == CreateSubscriptionsTable: reverted (0.1560s) =============================== == CreateSubjectsTable: reverting ============================================= -- drop_table(:subjects) -> 0.0000s -- remove_column(:documents, :subject_id) -> 0.1570s == CreateSubjectsTable: reverted (0.1570s) ==================================== == CreateUsersTable: reverting ================================================ -- drop_table(:users) -> 0.0000s -- remove_column(:documents, :user_id) -> 0.1400s == CreateUsersTable: reverted (0.1400s) ======================================= == AddDocsAttributes: reverting =============================================== -- remove_column(:documents, :created_at) -> 0.1250s -- remove_column(:documents, :updated_at) -> 0.1870s -- remove_column(:documents, :platform) -> 0.1260s == AddDocsAttributes: reverted (0.4380s) ====================================== == CreateDocsStore: reverting ================================================= -- drop_table(:documents) -> 0.0000s == CreateDocsStore: reverted (0.0000s) ======================================== |
我们介绍了如何使用 IBM_DB 适配器/驱动程序和 DB2 开始 Rails 应用程序开发。Starter Toolkit for DB2 on Rails 为那些不熟悉 DB2 和 Ruby on Rails 的人提供了最容易的方法,但是对于现有的 DB2 开发人员,IBM_DB 适配器和驱动程序 gem 或插件将提供同样容易的入门点。
我们还详细讨论了 Rails 迁移。管理数据库模式的更改是件乏味的事情。Rails 迁移特性可以帮助应用程序开发人员管理这种模式演变,便于在应用程序代码与数据库对象之间同步更改。通过利用 Rails 迁移和在 Ruby 文件中定义数据库模式更改,可以以逻辑的方式对更改进行版本控制。而且,由于 Rails 的 ActiveRecord 是独立于供应商的,开发人员只需创建一个 Ruby 迁移文件,就可以在多个使用不同 ActiveRecord 适配器的数据库平台上处理相同的更改。
请继续关注 Ruby on Rails 系列的第 2 部分,在第 2 部分中我们将展示如何将 DB2 on Rails 和 XML 支持提高到新的水平,进一步介绍通过 Ruby on Rails 操作 XML。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9403012/viewspace-87/,如需转载,请注明出处,否则将追究法律责任。