技术开发 频道

使用Ruby配备Oracle数据库


Ruby 与 Oracle 数据库交互

为了使 Ruby 能够与我们的 Oracle 数据库通信,需要使用 Ruby/OCI8。可从 http://rubyforge.org/projects/ruby-oci8/ 获得。尽管我们可以用交互式 IRB 编写自己的 Ruby 代码来访问数据库,但这种方法比较费事。较好的方法是:将我们的数据库访问代码编写成一个 Ruby 脚本文件,然后用 ruby 解释器命令运行该 Ruby 脚本文件。

Ruby 与 Oracle DDL SQL。 我们首先创建一个用于创建新表的 Ruby 脚本文件。我们需要声明与 Ruby/DBI 结合使用的是一个 require 语句,然后使用 Ruby/DBI API。清单 1 显示以上内容和 CREATE 语句。

清单1:createStatesTable.rb

            

require 'dbi'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

dbh.do("CREATE TABLE states (

id CHAR(2) PRIMARY KEY,

name VARCHAR2(15) NOT NULL,

capital VARCHAR2(25) NOT NULL)")

dbh.disconnect

清单 1 中的大多数代码都是您在 SQL*Plus 或 SQL Developer 中用于创建一个新数据库表的 SQL 代码。数据库连接字符串包括 DBI:OCI8:ORCL 字符串以及用户名和口令(本示例中为 hr/hr)。DBI 部分是标准内容,OCI8 部分指的是 Ruby/OCI8 驱动程序,ORCL 部分指的是数据库服务。可以使用简单的 createStatesTable.rb 命令在命令行执行这个 createStatesTable.rb 文件。如果一切顺利,则不会提供任何反馈,但该表将处于 HR 模式中。

继续介绍 Ruby 的其他数据库操作之前,需要指出 ruby 解释器命令允许您检查脚本的语法而无需实际运行它。例如,如果要测试 createStatesTable.rb 脚本中的语法而不实际创建该表,我可以使用 -c 选项,如下: ruby -c createStatesTable.rb.

清单 1 中与 Ruby 有关的另一件有趣的事情是未定义变量 dbh 的数据类型。与其他动态脚本编写语言一样,Ruby 不需要声明变量的数据类型,而是根据变量的上下文来假定所需的数据类型。在其余的 Ruby 代码清单中,我们将看到更多这样的“动态类型 (duck typing)”— 如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。

Ruby 与 Oracle DML SQL。 创建了一个 STATES 表之后,我们来填充它。我们将向该表中插入几个代表性的州名,如清单 2 所示。

清单2:populateStatesTable.rb

            

require 'dbi'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

sqlInsert = "INSERT INTO states (id, name, capital)

VALUES (?, ?, ?)"

dbh.do(sqlInsert, "AL", "Alabama", "Birmingham")

dbh.do(sqlInsert, "AZ", "Arizona", "Phoenix")

dbh.do(sqlInsert, "CO", "Colorado", "Denver")

dbh.do(sqlInsert, "FL", "Florida", "Tallahassee")

dbh.do(sqlInsert, "MA", "Maine", "Augusta")

dbh.do(sqlInsert, "PA", "Pennsylvania", "Philadelphia")

dbh.do(sqlInsert, "UT", "Utah", "Salt Lake City")

dbh.do(sqlInsert, "WA", "Washington", "Seattle")

dbh.do(sqlInsert, "WY", "Wyoming", "Cheyenne")

dbh.commit

dbh.disconnect

清单 2 中有一些需要了解的 Ruby/DBI 新特性。首先,注意问号在 INSERT 语句中用作占位符。这很有用,因为它允许创建该语句一次,然后针对插入到表中的每个代表性州名进行填充。由于 INSERT 语句是一个 DML 语句(与清单 1 中的 DDL CREATE 语句不同),因此在该清单中还需要一个 COMMIT 调用。

执行了这两个 Ruby 脚本之后,您就具有了一个名为 STATES 的数据库表,其中填充了几个州。现在,假设您在一些州的首府上犯了一些典型错误,甚至弄混了一个州的缩写(用 MA 代表 Maine,实际上 MA 代表 Massachusetts)。

清单 3 显示了用以修复这些问题的简单 UPDATE 脚本。清单 3 用占位符更新州首府并直接更新 Maine 的错误缩写,而不使用任何占位符或任何类型的语句准备。

清单3:updateStatesTable.rb

            

require 'dbi'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

sqlCapitalsUpdate = "UPDATE states SET capital = ? WHERE id = ?"

dbh.do(sqlCapitalsUpdate, "Montgomery", "AL")

dbh.do(sqlCapitalsUpdate, "Harrisburg", "PA")

dbh.do(sqlCapitalsUpdate, "Olympia", "WA")

dbh.do("UPDATE states SET id = 'ME' WHERE name = 'Maine'")

dbh.commit

dbh.disconnect

正如您希望的那样,DELETE 的执行方式与其同等的 DML 语句 INSERT 和 UPDATE 的执行方式非常类似。因此,您现在将转到一条 SELECT 语句,因为该语句添加了一个新技巧:数据库查询的结果集。

清单 4 演示如何在我们新填充的 STATES 表上执行一个简单查询。内置的 p 函数打印出返回的行,如图 5 所示。您可能已经使用内置的 puts 方法或 pp(“优化打印程序”)库来显示查询结果。清单 4 显示使用 # 指示有关打印查询结果的这两种方式的 Ruby 内联注释。

清单4:queryStatesTable.rb

            

require 'dbi'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

rs = dbh.prepare('SELECT * FROM states')

rs.execute

while rsRow = rs.fetch do

p rsRow

#Alternative output: puts rsRow

#Alternative output: pp rsRow

end

rs.finish

dbh.disconnect

图5:查询结果

Ruby DBI 提供了更好的输出格式。清单 5 和 6 显示使用 DBI 分别以表格和 XML 格式输出相同的查询结果。清单 5 和 6 中的代码不仅显示了 DBI::Utils 输出格式功能的用法,还显示了与清单 4 略微不同的获取和操作结果集的方法。

清单5:queryStatesTableFormatter.rb

            

require 'dbi'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

rs = dbh.execute('SELECT * FROM states')

rows = rs.fetch_all

column_names = rs.column_names

rs.finish

DBI::Utils::TableFormatter.ascii(column_names, rows)

dbh.disconnect

Listing 6: queryStatesTableXML.rb

require 'dbi'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

rs = dbh.execute('SELECT * FROM states')

states_rows = rs.fetch_all

rs.finish

DBI::Utils::XMLFormatter.table(states_rows)

dbh.disconnect

图 6 和 7 分别显示了这些脚本产生的表格式输出和 XML 输出。

图6:查询结果的表格式输出
图7:XML 格式的查询结果

您现在已经利用 Ruby 和 Oracle 执行了基本的 CRUD(CREATE、RETRIEVE、UPDATE 和 DELETE)操作。现在该介绍结合使用 Ruby 和 Oracle 存储过程的示例了。

Ruby 与 Oracle 存储过程。 PL/SQL 是许多基于 Oracle 数据库的应用程序频繁使用的语言。由于用户已经对 Oracle 存储过程投入了大量的资源、时间以及从实际经验中吸取的教训,因此您使用的任何语言可以访问大量已证明的功能是很重要的。

清单 7 显示用于访问 PL/SQL 内置存储过程 DBMS_UTILITY.DB_VERSION(接受两个 OUT 参数)的代码,您的代码显示通过这两个 OUT 参数传回的结果。

清单7:builtInDBVersionCompat.rb

            

require 'dbi'

db_read_str = 'BEGIN DBMS_UTILITY.DB_VERSION(?, ?); END;'

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

sth_db = dbh.prepare(db_read_str)

sth_db.bind_param(1, ' ' * 50) # allow for up to 50 chars

sth_db.bind_param(2, ' ' * 50) # allow for up to 50 chars

sth_db.execute

version = sth_db.func(:bind_value, 1)

puts "Oracle DB Version: " + version

compatibility = sth_db.func(:bind_value, 2)

puts "Oracle DB Compatibility: " + compatibility

dbh.disconnect

图 8 显示了运行该 Ruby 代码的显示结果。

图8:Ruby 访问内置 DBMS_UTILITY.DB_VERSION 的结果

清单 7 还有另一个需要注意的有趣地方。该清单演示了 Ruby 符号(本例中为 :bind_value)的使用,还显示了使用 DBI::Handle(本例中名为 sth_db)函数方法调用特定于该 Ruby DBI 数据库驱动程序的功能。

清单 8 中的代码演示了使用 Ruby DBI 运行内置的存储函数 DBMS_METADATA.GET_DDL。由于多方面的原因,这个内置函数很有用,下面有关如何获取 Ruby DBI 中某个函数的返回值的示例就是其中一个原因。该函数需要将对象类型和对象名与几个可选参数一起传入。在这种情况下,您只传入两个必需的参数,并使用名称批注传递它们。

清单8:builtInGetDDL.rb

            

require 'dbi'

db_read_str = 'BEGIN :out1 := DBMS_METADATA.GET_DDL(object_type=>:in1, '

db_read_str += 'name=>:in2); END;'

puts db_read_str

dbh = DBI.connect('DBI:OCI8:ORCL', 'hr', 'hr')

sth_db = dbh.prepare(db_read_str)

sth_db.bind_param("out1", ' ' * 20000 )

sth_db.bind_param("in1", "TABLE")

sth_db.bind_param("in2", "STATES")

sth_db.execute

puts sth_db.func("bind_value", "out1")

dbh.disconnect

DBMS_METADATA.GET_DDL 函数可返回创建我们指定的表所需的 DDL。注意,这是有用的信息,因为它为我们提供了我们重新创建表时将需要的 DDL。返回的 DDL 明显比我们的 CREATE TABLE 语句更具体,因为它显式说明了我们假定的设置。查看这些结果可以更深入地了解 Oracle 数据库的工作,帮助数据库专业人员决定要更改和调整的参数。

除了显示如何访问 Oracle 存储函数的返回值之外,清单 8 还显示了与我们先前使用的不同的绑定变量的方法。在之前具有绑定变量的清单(清单 7)中,我们使用的是位置绑定,这意味着您将问号 (?) 放在数据库执行字符串中,并使用从 1 开始的连续整数来引用值。在清单 8 中,您使用的是名称绑定,这意味着我们放入的是 Ruby 符号(如本例中的 :out1、:in1 和 :in2),稍后通过名称引用这些绑定变量以访问其值。Ruby 对位置绑定与名称绑定的支持与 PL/SQL 对位置参数批注和命名参数批注的支持类似。图 9 显示了运行清单 8(用于创建我们的 STATES 表的 DDL)的结果。

图9:DBMS_METADATA.GET_DDL 中的 DDL create 语句
0
相关文章