技术开发 频道

使用Ruby配备Oracle数据库


Ruby 异常处理

Ruby 具有一些可用于脚本和面向对象的应用程序的强大特性。其中一个特性是 Ruby 的异常处理。清单 12 包含使用 Ruby 的异常处理机制的示例。

清单12:rubyExceptions.rb

            

=begin

This script is similar to Listing 10, but demonstrates Ruby exception handling

using functionality originally demonstrated in Listing 10.

=end

fileName = ARGV.to_s

#obtain file name if not provided on command-line

if fileName.size < 1

print "Enter init.ora path and name: "

fileName = gets.chomp!

end

#ensure something has been provided for file name

begin

if fileName.size < 1

raise ArgumentError, "No valid file name provided" + "\n"

end

rescue ArgumentError => argErr

print argErr

raise # re-raise this exception and force script termination

end

#get file contents

begin #Begin exception handling block for file I/O

theFile = File.new(fileName, "r")

text = theFile.read

theFile.close

rescue IOError

print "I/O Error: Problem accessing file " + fileName + "\n"

exit

rescue Errno::ENOENT

print "ENOENT: Cannot find file " + fileName + "\n"

exit

rescue Errno::EPERM

print "EPERM: Insufficient rights to open " + fileName + "\n"

raise

rescue Exception # Catch-all: More exceptions captured than with "rescue" alone

print "Generic error rescued (captured) during file I/O attempt." + "\n"

raise

else

print "Good news! There was no problem with file I/O for " + fileName + "\n"

ensure

print "Good or bad, file handling attempt is now complete!\n"

end #End exception handling block for file I/O

#obtain text string for regular expression

print "Enter regular expression pattern: "

pattern = gets.chomp!

begin #Begin exception handling block for regular expression

regExp = Regexp.new(pattern)

rescue RegexpError

print "Problem with regular expression " + regExp.to_s + "\n"

exit # Nothing to be done with a bad regular expression

ensure

print "Regular expression evaluated was: " + regExp.to_s + "\n"

end #End exception handling block for regular expression

puts text.scan(regExp)

清单 12 演示了使用 begin 关键字标记可以引发(抛出)异常的代码块的起始位置。该清单中使用关键字 raise 供脚本引发(抛出)自己的异常。关键字 rescue 多次出现,与 Java 的 catch 关键字类似。Ruby 的 ensure 关键字与 Java 的 finally 关键字类似,在该代码清单中用于执行功能,无论是否遇到异常。

如清单 12 所示,Ruby 还支持 else 关键字涵盖以下情况下应该执行的功能:如果未针对由 begin 和 end 指示的特定代码块抛出任何异常。代码清单的注释进一步解释了 Ruby 的异常处理功能。

图 11 显示了运行清单 12 中的脚本三次的结果。第一次运行该脚本时,故意提供了一个未知的文件名以调用文件处理异常处理。第二次运行该脚本时用正确的文件名演示 else 块的执行,并允许执行转至与正则表达式处理相关的代码块。第二次执行该脚本时,提供了一个错误的表达式以演示 RegexpError 的处理。第三次运行该脚本时演示了脚本的完整运行,不引发异常。所有这三次运行都演示了 ensure 块的执行,因为无论是否引发(抛出)异常,所有异常处理过程中始终调用该块。

图11:正在运行的 Ruby 异常

Ruby DBI 异常处理。 最后一部分介绍了 Ruby 异常处理。Ruby/DBI 提供了自己的异常处理层次结构,如清单 13 所示。实际上,较常见的是只捕获“database catch all”异常 DBI::DatabaseError 并访问清单 13 中所显示的属性,以打印出数据库错误代码和错误消息。

清单13:rubyDbExceptions.rb

            

=begin

Listing13-rubyDbExceptions.rb

This script demonstrates the use of exceptions with DBI::DatabaseError

and database-related exception classes that inherit from DBI::DatabaseError.

Often, it is enough to capture DatabaseError, but this example shows all

of the inherited exception classes. DatabaseError is rescued last so that

a more particular database-related exception might be rescued first.

This "try" block of this code includes an "infinite" while loop that will

open connections without closing them until a DBI::DatabaseError is forced.

=end

require 'dbi'

begin

counter = 0

while 0 # "infinite" loop because 0 resolves to "true" for Ruby conditional

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

counter += 1

puts "DB Connection #" + counter.to_s + "\n"

#Intentionally NOT closing with dbh.close to force DatabaseError.

end

rescue DBI::DataError => dataErr

dbh.rollback

puts "DB error due to problem with data"

puts "Error Code: #{dataErr.err}"

puts "Error Message: #{dataErr.errstr}"

puts "DB rollback.\n"

rescue DBI::IntegrityError => integErr

# Example: Trying to insert same value for unique column twice.

dbh.rollback

puts "DB error due to integrity problem."

puts "Error Code: #{integErr.err}"

puts "Error Message: #{integErr.errstr}"

puts "DB rollback.\n"

rescue DBI::InternalError => internErr

dbh.rollback

puts "DB error database internal error."

puts "Error Code: #{internErr.err}"

puts "Error Message: #{internErr.errstr}"

puts "DB rollback.\n"

rescue DBI::NotSupportedError => notSuppErr

dbh.rollback

puts "DB feature not supported."

puts "Error Code: #{notSuppErr.err}"

puts "Error Message: #{notSuppErr.errstr}"

puts "DB rollback.\n"

rescue DBI::OperationalError => opErr

dbh.rollback

puts "DB error due to problems with operation of database."

puts "Error Code: #{opErr.err}"

puts "Error Message: #{opErr.errstr}"

puts "DB rollback.\n"

rescue DBI::ProgrammingError => dbProgErr

# Example: Bad column name in SQL statement.

dbh.rollback

puts "DB error due to programming problem.\n"

puts "Error Code: #{dbProgErr.err}"

puts "Error Message: #{dbProgErr.errstr}"

puts "DB rollback.\n"

rescue DBI::DatabaseError => dbErr

# Catch-all for all database exceptions.

dbh.rollback

puts "Database exception encountered."

puts "Error Code: #{dbErr.err}"

puts "Error Message: #{dbErr.errstr}"

puts "DB rollback."

rescue DBI::InterfaceError => ifError

dbh.rollback

puts "Problem with DBI interface encountered."

rescue RuntimeError

dbh.rollback

puts "Unknown error (not DB or DBI) encountered."

else

puts "DB commit.\n"

dbh.commit

ensure

puts "Disconnecting database handler."

dbh.disconnect

end

清单 13 中显示的其他 DBI 异常包括那些派生自 DBI::DatabaseError 的异常,以及用于捕获与 DBI 接口而非数据库有关的异常的 DBI::InterfaceError 异常。

清单 13 中的代码将导致抛出一个异常,因为存在一个“while 0”循环(Ruby 中 0 的值为“true”),该循环将重复执行,直至抛出异常。由于在抛出 DBI::DatabaseError 之前重复调用 DBI.connect 而没有正确关闭打开的连接,因此的确会抛出该异常。在这种情况下,显示的错误代码为 12520(特定于 Oracle 的数据库错误代码),错误字符串为“ORA-12520:TNS:listener could not find available handler for requested type of server.”这些输出代码和字符串值是使用适当的 DBI::DatabaseError 属性(err 和 errstr)输出的。

如果用一个数据库操作(如 SELECT 语句)替换清单 13 中的 while 循环(故意强制数据库错误),rescue、else 和 ensure 块将和清单 12 中的对应块一样被激活。如果仍出现任何异常(如完整性约束),将调用适当的“rescue”块。如果没有遇到指定了 rescue 块的异常,“else”块中的代码将执行并提交事务。在任何情况下,无论是否抛出异常,都将执行“ensure”代码块,并相应地断开与处理程序的连接。

编写 Ruby 脚本时,不一定需要捕获 (rescue) 异常(考虑 Java 的非强制异常)。清单 12 之前的所有未进行异常处理的代码清单都对此进行了阐释。然而,如果您知道可能出现某些异常并希望您的脚本针对这些情况进行某些处理(如立即退出或者打印出某些与异常相关的详细信息),Ruby 可以简化异常处理。如果您不想将 rescue-else-ensure 块与抛出异常的代码块相关联,该脚本将突然停止该代码块的执行,将显示常规异常信息,执行下一个代码块。

图 12 显示了 Ruby 的一些主要异常。图 13 显示了 Ruby DBI 异常。除了这些标准异常之外,您始终可以创建和使用自己的 Ruby 异常。

图12:所选的 Ruby 异常
图13:Ruby/DBI 异常

由于 Ruby(与 Java 一样)是一种面向对象的语言,因此我能够用 Oracle JDeveloper 10g 的 UML 建模工具创建创建图 12 和 13 中的类图。由于 DatabaseError 属性用在代码清单中,因此在图 13 中专门调用这些属性。

0
相关文章