原著 Paul DuBois paul@kitebird.com
翻譯:liubin 2004/11/9 http://www.ruby-cn.org/
原文地址:http://www.kitebird.com/articles/ruby-dbi.html
文檔版本: 1.02
最后更新: 2003-05-27
Ruby DBI模塊為ruby程序訪問數(shù)據(jù)庫(kù)提供了一個(gè)與數(shù)據(jù)庫(kù)無(wú)關(guān)的接口,就像perl的DBI模塊一樣。這篇文章將講述如何編寫基于DBI的ruby程序。這篇文章是對(duì)DBI規(guī)范文檔(specification documents)的補(bǔ)充,而不是要替代規(guī)范文檔,更多的信息請(qǐng)參見“資源”一節(jié)。
Ruby的DBI 模塊架構(gòu)分為兩層:
本文的例子用到的數(shù)據(jù)庫(kù)都是mysql的,但多數(shù)也可以適用其他數(shù)據(jù)庫(kù)驅(qū)動(dòng)。
Ruby DBI模塊包括了實(shí)現(xiàn)一般DBI的代碼,和一些DBD層的驅(qū)動(dòng),很多這些驅(qū)動(dòng)需要你安裝額外的軟件。比如,用于Mysql的驅(qū)動(dòng)使用ruby寫成,與ruby mysql模塊綁定,而ruby mysql驅(qū)動(dòng)是c語(yǔ)言寫的,幫定了mysql 的c語(yǔ)言 客戶端API。這就是說(shuō),你要是想用DBI訪問MySql數(shù)據(jù)庫(kù),ruby mysql模塊和C API這兩者都需要安裝。更多關(guān)于ruby mysql 模塊的信息,參見“資源”一節(jié)。這里我們假定你已經(jīng)安裝了ruby mysql,并且可以用于DBI。
一旦你滿足了前面的條件,就可以安裝Ruby DBI模塊,可以從這里取得:
http://ruby-dbi.sourceforge.net/
DBI模塊以壓縮的tar格式發(fā)布,下載之后應(yīng)該解壓縮,比如,現(xiàn)在版本是0.0.19,如下即可解壓縮:
% tar zxf ruby-dbi-all-0.0.19.tar.gz % gunzip < ruby-dbi-all-0.0.19.tar.gz | tar xf -
解壓縮之后,進(jìn)入軟件包的頂層目錄下,用setup.rb腳本進(jìn)行配置。一般的配置命令都像這樣,在config后面沒有參數(shù):
% ruby setup.rb config
這條命令設(shè)置了默認(rèn)安裝所有的驅(qū)動(dòng),更有效的辦法是在剛才的config 后面加上--with參數(shù),指定需要安裝的部分。比如,為了配置只安裝主DBI模塊和MYSQL DBD 驅(qū)動(dòng),運(yùn)行下面命令:
% ruby setup.rb config --with=dbi,dbd_mysql
配置完要安裝的軟件之后,就可以build和安裝了:
% ruby setup.rb setup % ruby setup.rb install
運(yùn)行install需要root權(quán)限。
本文的后面部分將使用下面的表示約定:
安裝完ruby DBI模塊之后,你就可以在你的Ruby程序中訪問MYSQL數(shù)據(jù)庫(kù)了。假設(shè)我們的數(shù)據(jù)庫(kù)在本機(jī)運(yùn)行,即localhost,數(shù)據(jù)庫(kù)名為test,通過(guò)一個(gè)用戶名為testuser,密碼是testpass的用戶訪問。我們可以用root登陸到mysql程序,然后執(zhí)行下列命令建立這樣的一個(gè)用戶:
mysql> GRANT ALL ON test.* TO 'testuser'@'localhost' IDENTIFIED BY 'testpass';
如果test數(shù)據(jù)庫(kù)不存在,用下面的命令創(chuàng)建它:
mysql> CREATE DATABASE test;
如果你想用不同的數(shù)據(jù)庫(kù),服務(wù)器,用戶和密碼的話,只需要將例子里對(duì)應(yīng)的值換成你自己的就行了。
下面這個(gè)腳本, simple.rb, 是一個(gè)很短的DBI程序,它先連接的數(shù)據(jù)庫(kù),然后查詢了數(shù)據(jù)庫(kù)的版本,并顯示出來(lái),然后斷開連接。你可以從“資源”里提供的鏈接下載這段代碼,或者把它拷貝到文本編輯器中:
# simple.rb - simple MySQL script using Ruby DBI module
require "dbi"
begin # connect to the MySQL server
dbh = DBI.connect("dbi:Mysql:test:localhost", "testuser", "testpass") # get server version string and display it
row = dbh.select_one("SELECT VERSION()")
puts "Server version: " + row[0]
rescue DBI::DatabaseError => e
puts "An error occurred"
puts "Error code: #{e.err}"
puts "Error message: #{e.errstr}" ensure # disconnect from server
dbh.disconnect if dbh
end
simple.rb 展現(xiàn)了DBI最基本的一些概念,下面的討論將會(huì)講述他是如何工作的,然后更后面還要講述DBI的其他一些方面。
simple.rb以一行require 開始,把DBI模塊引入近來(lái);沒有這一行的話,DBI方法將會(huì)出錯(cuò),后面的代碼包括在一個(gè) begin/rescue/ensure 結(jié)構(gòu)中:
方法connect 用來(lái)和數(shù)據(jù)庫(kù)服務(wù)器建立一個(gè)連接,并返回這個(gè)連接。第一個(gè)參數(shù)是數(shù)據(jù)源名(data source name DSN),它指定了驅(qū)動(dòng)名稱(Mysql用于MySql服務(wù)器),默認(rèn)得數(shù)據(jù)庫(kù)名和服務(wù)器的機(jī)器名,第二、三個(gè)參數(shù)是用戶和密碼。還有其他的DSN寫法, 將在后面“再論連接數(shù)據(jù)庫(kù)”中說(shuō)明。
simple.rb 用數(shù)據(jù)庫(kù)句柄(database handle)調(diào)用方法select_one, 這個(gè)方法向服務(wù)器發(fā)送一個(gè)查詢語(yǔ)句,并且將結(jié)果集的第一行作為數(shù)組返回給調(diào)用者?!?tt>SELECT VERSION() ”返回返回單個(gè)值,所以版本信息將存在row[0]中,這是這個(gè)數(shù)組的第一個(gè)也是唯一一個(gè)元素。運(yùn)行這個(gè)程序,結(jié)果像這樣:
% ruby simple.rb Server version: 4.0.13-log
如果出錯(cuò),會(huì)導(dǎo)致拋出異常,異常可能各種各樣,但多數(shù)都屬于數(shù)據(jù)庫(kù)錯(cuò)誤,多為DatabaseError 異常,這種異常對(duì)象包括err和errstr屬性,err是錯(cuò)誤編號(hào),errstr是錯(cuò)誤消息。simple.rb得到這些異常的值并打印它們,但是忽略了其他的異常,這時(shí)候如果出現(xiàn)了其他異常,則將會(huì)拋給ruby執(zhí)行環(huán)境。
simple.rb 用disconnect方法來(lái)斷開與數(shù)據(jù)庫(kù)的連接,這在ensure里執(zhí)行,這樣就使得不管出錯(cuò)與否,數(shù)據(jù)庫(kù)連接都會(huì)被斷開。
Ruby DBI 提供了很多方法用來(lái)執(zhí)行查詢語(yǔ)句。這里將討論這中間的一部分,但還有其他的。
多數(shù)的例子都用到了表people,它的結(jié)構(gòu)如下:
CREATE TABLE people (
id INT UNSIGNED NOT NULL AUTO_INCREMENT, # ID number
name CHAR(20) NOT NULL, # name
height FLOAT, # height in inches
PRIMARY KEY (id) );
如果一個(gè)語(yǔ)句不需要返回結(jié)果,可以用數(shù)據(jù)庫(kù)句柄的do方法,這個(gè)方法的參數(shù)為要執(zhí)行的sql語(yǔ)句,返回受影響的行數(shù)。下面的例子創(chuàng)建了表people,并插入了幾條記錄,都用了do方法:
需要注意的是insert語(yǔ)句返回了一個(gè)值,即插入的行數(shù),并把它打印了出來(lái)。
像select和show這樣的語(yǔ)句是要返回行記錄的,處理這樣的語(yǔ)句,要先向服務(wù)器提交查詢,處理查詢產(chǎn)生的每條記錄,然后把結(jié)果集銷毀。
一種辦法是調(diào)用prepare產(chǎn)生一個(gè)statement 句柄,用這個(gè)句柄來(lái)執(zhí)行查詢,取回結(jié)果,然后釋放結(jié)果集:
sth = dbh.prepare(statement) sth.execute ... fetch rows ... sth.finish
或者直接把語(yǔ)句發(fā)送給數(shù)據(jù)庫(kù)連接句柄去執(zhí)行而不用調(diào)用prepare:
sth = dbh.execute(statement) ... fetch rows ... sth.finish
同樣也有很多方法從執(zhí)行完的語(yǔ)句取得結(jié)果,可以在一個(gè)循環(huán)里調(diào)用fetch方法直到返回nil為止:
sth = dbh.execute("SELECT * FROM people")
while row = sth.fetch do
printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2]
end
sth.finish
fetch 也可以用作一個(gè) iterator來(lái)用, 也用each.方法。下面的兩個(gè)是一樣的作用:
fetch 和each 都產(chǎn)生了 DBI::Row 對(duì)象, 這個(gè)對(duì)象提供了訪問他們內(nèi)容的方法:
val = row.by_index(2) val = row.by_field("height")
val = row[2] val = row["height"]
sth = dbh.execute("SELECT * FROM people")
sth.each do |row|
row.each_with_name do |val, name|
printf "%s: %s, ", name, val.to_s
end
print "\n"
end
sth.finish
其他的返回行數(shù)據(jù)的方法包括fetch_array和fetch_hash,他們不返回DBI::Row對(duì)象,而是將下一行數(shù)據(jù)作為數(shù)組或者哈希返回, 如果已經(jīng)到結(jié)果集的最后的話,也會(huì)返回nil。fetch_hash返回哈希結(jié)構(gòu),由列名作為key,而列的值作為這個(gè)key對(duì)應(yīng)的值。這兩個(gè)方法可以獨(dú)立使用,也可以在迭代中使用。下面例子用了hash方法:
sth = dbh.execute("SELECT * FROM people") while row = sth.fetch_hash do printf "ID: %d, Name: %s, Height: %.1f\n", row["id"], row["name"], row["height"] end sth.finish sth = dbh.execute("SELECT * FROM people") sth.fetch_hash do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row["id"], row["name"], row["height"] end sth.finish
你也可以不用依照 “查詢--取結(jié)果--完成”這種順序來(lái)執(zhí)行你的語(yǔ)句,數(shù)據(jù)庫(kù)句柄可以一次取回所有的結(jié)果:
row = dbh.select_one(statement) rows = dbh.select_all(statement)
select_one 執(zhí)行一個(gè)查詢,然后將結(jié)果的第一行作為一個(gè)數(shù)組返回,或者返回nil,如果沒有匹配記錄的話。select_all 返回一個(gè) DBI::Row 的數(shù)組,(你可以用前面討論過(guò)得方法訪問里面的內(nèi)容)。如果沒有匹配結(jié)果,則返回空數(shù)組。注意不是nil。
MySQL 驅(qū)動(dòng)會(huì)檢查返回的結(jié)果集中的元數(shù)據(jù)(metadata),然后強(qiáng)制將這個(gè)字段的的值變?yōu)閷?duì)應(yīng)的Ruby類型(比如,從people取得的id,name,height字段的值將會(huì)被轉(zhuǎn)變?yōu)镕ixnum,String和Float對(duì)象)。但是,如果一個(gè)列的值為NULL,則用nil來(lái)表示,并且它的類型為NilClass。還有就是這不是DBI規(guī)格說(shuō)明書的硬性規(guī)定,所以有的驅(qū)動(dòng)可能不會(huì)做這樣的工作。
Ruby DBI提供了占位符機(jī)制,使得你可以不用在查詢語(yǔ)句中把數(shù)據(jù)值的字面值寫到里面,而是用一些特殊的符號(hào)標(biāo)記數(shù)據(jù)的位置,當(dāng)你真的要執(zhí)行的時(shí)候,用真實(shí)的數(shù)據(jù)值填充占位符的位置。DBI會(huì)用數(shù)據(jù)值替換占位符,完成對(duì)字符串等加引號(hào),特殊字符的轉(zhuǎn)義(如果需要的話)等,而不必你自己去做,而且占位符機(jī)制能很好的處理NULL值,你只需要提供一個(gè)nil值,它會(huì)自動(dòng)被換成NULL放到查詢中。
下面例子解釋了它是如何工作的。加入你想向people表里插入一條記錄,這個(gè)人的名字叫Na'il,這個(gè)名字包括一個(gè)單引號(hào),他的身高是76英寸。在查詢語(yǔ)句中,用?來(lái)作為插入值的占位符,不需要引號(hào)括起來(lái),然后將實(shí)際要插入的值作為do的參數(shù),如下:
dbh.do("INSERT INTO people (id, name, height) VALUES(?, ?, ?)", nil, "Na'il", 76)
這條語(yǔ)句發(fā)送給數(shù)據(jù)庫(kù)的語(yǔ)句像這樣:
INSERT INTO people (id,name,height) VALUES(NULL,'Na\'il',76)
這更適合于你要多次執(zhí)行一個(gè)查詢,你可以先生成一個(gè)預(yù)處理語(yǔ)句語(yǔ)句,然后每次用數(shù)據(jù)值填充去執(zhí)行。假如要導(dǎo)入的數(shù)據(jù)存在文本文件people.txt里面,每一行了用tab分割,由name,height兩列組成,下面的代碼演示了如何從數(shù)據(jù)文件讀取數(shù)據(jù),然后執(zhí)行insert語(yǔ)句將每一行插入數(shù)據(jù)庫(kù):
# prepare statement for use within insert loop sth = dbh.prepare("INSERT INTO people (id, name, height) VALUES(?, ?, ?)") # read each line from file, split into values, and insert into database f = File.open("people.txt", "r") f.each_line do |line| name, height = line.chomp.split("\t") sth.execute(nil, name, height) end f.close
生成一個(gè)預(yù)處理語(yǔ)句,然后在循環(huán)中多次執(zhí)行它,比用循環(huán)來(lái)直接執(zhí)行有效多了,主要是因?yàn)閿?shù)據(jù)庫(kù)能為預(yù)處理語(yǔ)句生成一個(gè)執(zhí)行計(jì)劃,以后每次執(zhí)行都會(huì)用這個(gè)執(zhí)行計(jì)劃來(lái)執(zhí)行,提高了效率。當(dāng)然目前mysql還不支持這個(gè)功能,oracle支持。
如果想用占位符的方法執(zhí)行select語(yǔ)句,你應(yīng)該先考慮一下是否用預(yù)處理語(yǔ)句:
sth = dbh.prepare("SELECT * FROM people WHERE name = ?") sth.execute("Na'il") sth.fetch do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finish
sth = dbh.execute("SELECT * FROM people WHERE name = ?", "Na'il") sth.fetch do |row| printf "ID: %d, Name: %s, Height: %.1f\n", row[0], row[1], row[2] end sth.finish
其它的驅(qū)動(dòng)也許需要用不同的占位符,比如你可能需要寫 :name 或 :n 來(lái)指明是按名稱還是按位置來(lái)對(duì)應(yīng)。
方法quote 能將一個(gè)值中的特殊字符處理、轉(zhuǎn)義等,并返回這個(gè)結(jié)果。這適用于產(chǎn)生sql語(yǔ)句以供別的程序使用,比如,你想將上面的people.txt文件的內(nèi)容轉(zhuǎn)化為能在mysql命令行里執(zhí)行的一組insert語(yǔ)句,只需要如下程序:
# read each line from file, split into values, and write INSERT statement f = File.open("people.txt", "r") f.each_line do |line| name, height = line.chomp.split("\t") printf "INSERT INTO people (id, name, height) VALUES(%s, %s, %s);\n", dbh.quote(nil), dbh.quote(name), dbh.quote(height) end f.close
對(duì)于不需要返回結(jié)果的語(yǔ)句,比如insert,delete等,do方法返回insert或者delete的行數(shù)。
對(duì)于返回結(jié)果的查詢,比如select,你可以在execute方法之后用statement句柄取得返回的行和列的個(gè)數(shù),以及各列的信息:
下面例子說(shuō)明了如何從一個(gè)查詢得到metadata:
sth = dbh.execute(query) puts "Query: " + query if sth.column_names.size == 0 then puts "Query has no result set" printf "Number of rows affected: %d\n", sth.rows else puts "Query has a result set" rows = sth.fetch_all printf "Number of rows: %d\n", rows.size printf "Number of columns: %d\n", sth.column_names.size sth.column_info.each_with_index do |info, i| printf "--- Column %d (%s) ---\n", i, info.name printf "precision: %s\n", info.precision printf "scale: %s\n", info.scale end end sth.finish
注意:本文檔的早期版本中說(shuō)你可以從sth.rows得到返回的行數(shù),現(xiàn)在已經(jīng)不支持了。(盡管現(xiàn)在在mysql驅(qū)動(dòng)中還可以用,但是你不應(yīng)該在依賴這個(gè)函數(shù)了)
一些能產(chǎn)生句柄的方法可以用來(lái)在block中調(diào)用,用這種方法時(shí),它們將句柄作為參數(shù)提供給block,并且在塊結(jié)束后自動(dòng)銷毀這些句柄。
下面的例子說(shuō)明了上面的三個(gè)問題:
# connect can take a code block, passes the database handle to it, # and automatically disconnects the handle at the end of the block DBI.connect("dbi:Mysql:test:localhost", "testuser", "testpass") do |dbh| # prepare can take a code block, passes the statement handle # to it, and automatically calls finish at the end of the block dbh.prepare("SHOW DATABASES") do |sth| sth.execute puts "Databases: " + sth.fetch_all.join(", ") end # execute can take a code block, passes the statement handle # to it, and automatically calls finish at the end of the block dbh.execute("SHOW DATABASES") do |sth| puts "Databases: " + sth.fetch_all.join(", ") end end
此外還有一個(gè) transaction 方法可以接收一個(gè)塊,將在下面的“事務(wù)處理支持”中討論。
前面討論過(guò)的simple.rb 腳本用DBI 的connect方法連接數(shù)據(jù)庫(kù)服務(wù)器:
dbh = DBI.connect("dbi:Mysql:test:localhost", "testuser", "testpass")
connect的第一個(gè)參數(shù)十DSN,它指明了要連接類型,后面的參數(shù)是用戶名和密碼。
DSN 可以是下面的任何格式的一種:
dbi:driver_name dbi:driver_name:db_name:host_name dbi:driver_name:key=val;key=val...
DSN總是以dbi或者DBI(而不能既有大寫又有小寫的字母)和驅(qū)動(dòng)名稱開頭,對(duì)MySql來(lái)說(shuō),驅(qū)動(dòng)名稱是Mysql,對(duì)于其他的驅(qū)動(dòng),需要指定對(duì)應(yīng)的正確的名字。
DSN中必須有dbi (或 DBI) ,如果在驅(qū)動(dòng)后面沒有其他信息,那么驅(qū)動(dòng)會(huì)嘗試用默認(rèn)得數(shù)據(jù)庫(kù)和機(jī)器名連接數(shù)據(jù)庫(kù)。而mysql要求必須指定數(shù)據(jù)庫(kù)名,所以上面的第一種寫法不能用于mysql,必須用其他的寫法。第二種寫法需要兩個(gè)值,一個(gè)數(shù)據(jù)庫(kù)名,一個(gè)機(jī)器名,兩個(gè)值用冒號(hào)分開。第三種格式允許用 param=value 格式指定一系列的參數(shù),參數(shù)之間用分號(hào)分割,比如,下面三種寫法完全等同:
dbi:Mysql:test:localhost dbi:Mysql:host=localhost;database=test dbi:Mysql:database=test;host=localhost
在 DSN 語(yǔ)法中使用 param=value 格式比較靈活,各個(gè)參數(shù)的位置可以隨意設(shè)置。而且可以設(shè)置一些針對(duì)不同驅(qū)動(dòng)的特有的參數(shù),就是說(shuō)可以在它接收的參數(shù)方面進(jìn)行擴(kuò)展。比如Mysql,除了host和database參數(shù),還可以設(shè)置port,socket,flag等參數(shù)。(這些參數(shù)對(duì)應(yīng)于ruby mysql 模塊的real_connect方法中的各個(gè)參數(shù),而DBD::Mysql也是基于這個(gè)Ruby Mysql模塊的)
如果一個(gè)DBI方法是白了,將拋出一個(gè)異常。DBI方法可以拋出幾種異常,但是和數(shù)據(jù)庫(kù)相關(guān)的方法一般拋出DatabaseError異常,這種異常的對(duì)象有三個(gè)屬性,err,errstr和state。DBI的文檔沒有說(shuō)這三個(gè)屬性是什么意思,但是看起來(lái)它們分別表示錯(cuò)誤編號(hào),一個(gè)字符串型的錯(cuò)誤描述和一些“標(biāo)準(zhǔn)”的錯(cuò)誤代碼。目前MySQL驅(qū)動(dòng)只支持errstr,但很容易用補(bǔ)丁使它也支持err屬性。假定這兩個(gè)屬性都可用,那么下面方法說(shuō)明了如何得到這些值:
rescue DBI::DatabaseError => e puts "An error occurred" puts "Error code: #{e.err}" puts "Error message: #{e.errstr}"
為了得到你的語(yǔ)句執(zhí)行時(shí)的調(diào)試信息,可以使用跟蹤(tracing)。要想這樣,首先你要載入dbi/trace模塊:
require "dbi/trace"
模塊 dbi/trace 默認(rèn)沒有包括在dbi模塊中,因?yàn)檫@需要0.3.3以上版本的AspectR模塊,這個(gè)模塊可能在你的機(jī)器上并不存在。
dbi/trace 模塊提供了一個(gè)trace方法,可以用來(lái)控制跟蹤模式和輸出目標(biāo):
trace(mode, destination)
mode 值為0(off),1,2,3,默認(rèn)值為2; destination 是一個(gè)IO對(duì)象,默認(rèn)為STDERR。
trace 可以作為一個(gè)類方法調(diào)用,這樣隨后創(chuàng)建的句柄都可以使用;或者作為一個(gè)單獨(dú)的驅(qū)動(dòng),數(shù)據(jù)庫(kù),statement 句柄的對(duì)象方法,任何繼承這些對(duì)象的子類都可以繼承這些跟蹤設(shè)置。比如,比如,你允許一個(gè)數(shù)據(jù)庫(kù)句柄進(jìn)行跟蹤,從這個(gè)句柄創(chuàng)建的statement句柄也具備同樣的跟蹤設(shè)置。
DBI提供了事務(wù)支持,但是怎樣支持取決于你的底層數(shù)據(jù)庫(kù)和DBD層數(shù)據(jù)庫(kù)驅(qū)動(dòng)的實(shí)現(xiàn)情況。比如Mysql驅(qū)動(dòng),在DBI 0.0.19之前都沒有提供,所以你必須使用statement的自動(dòng)提交功能來(lái)達(dá)到同樣的目的,比如:
dbh.do("SET AUTOCOMMIT=0") dbh.do("BEGIN") ... statements that make up the transaction ... dbh.do("COMMIT")
對(duì)于 DBI 0.0.19 和更高版本,你可以使用mysql的事務(wù)控制,可以設(shè)置數(shù)據(jù)庫(kù)句柄來(lái)設(shè)置是否自動(dòng)提交:
dbh['AutoCommit'] = true dbh['AutoCommit'] = false
當(dāng)自動(dòng)提交被禁止之后,你有兩種方法來(lái)實(shí)現(xiàn)事務(wù)控制。下面的例子說(shuō)明了這兩種方法,一個(gè)表account,要在兩個(gè)人時(shí)間的基金轉(zhuǎn)帳中實(shí)現(xiàn)事務(wù)性操作:
dbh['AutoCommit'] = false begin dbh.do("UPDATE account SET balance = balance - 50 WHERE name = 'bill'") dbh.do("UPDATE account SET balance = balance + 50 WHERE name = 'bob'") dbh.commit rescue puts "transaction failed" dbh.rollback end
dbh['AutoCommit'] = false dbh.transaction do |dbh| dbh.do("UPDATE account SET balance = balance - 50 WHERE name = 'bill'") dbh.do("UPDATE account SET balance = balance + 50 WHERE name = 'bob'") end
DBI提供了一個(gè)func方法,可以執(zhí)行不同數(shù)據(jù)庫(kù)驅(qū)動(dòng)特有的功能,比如,mysql C API提供了mysq_insert_id()方法,這個(gè)方法返回AUTO_INCREMENT 的最新值。Ruby Mysql模塊提供了一個(gè)綁定到這個(gè)函數(shù)的函數(shù):數(shù)據(jù)庫(kù)句柄的insert_id 方法。這個(gè)方法是在DBD::Mysql中提供的,使得你可以通過(guò)DBI訪問。
func 的第一個(gè)參數(shù)是你想執(zhí)行的數(shù)據(jù)庫(kù)特有的方法的名稱,后面的參數(shù)是這個(gè)數(shù)據(jù)庫(kù)特有方法的參數(shù),如果沒有參數(shù),可以不填。insert_id沒有參數(shù),所以要想訪問最新的AUTO_INCREMENT 值,可以這樣:
dbh.do("INSERT INTO people (name,height) VALUES('Mike',70.5)") id = dbh.func(:insert_id) puts "ID for new record is: " + id.to_s
DBD::Mysql 提供的其它方法包括:
dbh.func(:createdb, db_name) 創(chuàng)建數(shù)據(jù)庫(kù) dbh.func(:dropdb, db_name) 刪除數(shù)據(jù)庫(kù) dbh.func(:reload) 重新加載(reload) dbh.func(:shutdown) 關(guān)閉數(shù)據(jù)庫(kù)
注意的是,只有你的mysql版本在4以上,創(chuàng)建數(shù)據(jù)庫(kù)和刪除數(shù)據(jù)庫(kù)的功能才可以使用。
有些時(shí)候,使用數(shù)據(jù)庫(kù)特有的方法能有特別的有點(diǎn),即使按通常的其他方法也能達(dá)到同樣的作用。比如,DBD::Mysql 的insert_id方法的功能和執(zhí)行查詢語(yǔ)句“SELECT LAST_INSERT_ID()”一樣,都返回同一個(gè)值,但是insert_id更有效,因?yàn)樗堰@個(gè)值保存在了客戶端,再次需要時(shí)不用重復(fù)執(zhí)行查找。每次有新的插入之后,這個(gè)值都會(huì)改變,所以你必須重新得到這個(gè)AUTO_INCREMENT 值。與此相對(duì),LAST_INSERT_ID() 的結(jié)果保存在服務(wù)器上,所以是持久穩(wěn)固的,它不會(huì)因?yàn)閯e的查詢語(yǔ)句執(zhí)行而改變。
模塊DBI::Utils 包含了其他一些有趣的方法(包括子模塊中的方法):
elapsed = DBI::Utils::measure do dbh.do(query) end puts "Query: " + query puts "Elapsed time: " + elapsed.to_s
sth = dbh.execute("SELECT * FROM people") rows = sth.fetch_all col_names = sth.column_names sth.finish DBI::Utils::TableFormatter.ascii(col_names, rows)輸出結(jié)果如下:
+----+---------+--------+ | id | name | height | +----+---------+--------+ | 1 | Wanda | 62.5 | | 2 | Robert | 75.0 | | 3 | Phillip | 71.5 | | 4 | Sarah | 68.0 | +----+---------+--------+
DBI::Utils::XMLFormatter.table(dbh.select_all("SELECT * FROM people"))輸出結(jié)果如下:
<?xml version="1.0" encoding="UTF-8" ?> <rows> <row> <id>1</id> <name>Wanda</name> <height>62.5</height> </row> <row> <id>2</id> <name>Robert</name> <height>75.0</height> </row> <row> <id>3</id> <name>Phillip</name> <height>71.5</height> </row> <row> <id>4</id> <name>Sarah</name> <height>68.0</height> </row> </rows>
方法 ascii 和table 支持更多的參數(shù)以提供對(duì)結(jié)果的更多控制和更多的格式和輸出方式,可以參看這個(gè)模塊的源代碼獲取更多信息。
本文用到的腳本可以從下面的地址下載:
你會(huì)發(fā)現(xiàn)下面這些額外資源對(duì)你很好的使用Ruby DBI很有幫助:
- 你可以從DBI sourceforge得到Ruby DBI模塊和規(guī)范說(shuō)明書:
http://ruby-dbi.sourceforge.net/- 如果你想用dbi/trace模塊來(lái)使用DBI執(zhí)行tracing的話,必須安裝AspectR Ruby 模塊 。 AspectR 可以在sourceforge得到:
http://aspectr.sourceforge.net/- Ruby主頁(yè),提供了關(guān)于ruby很全面的信息:
http://www.ruby-lang.org/- 可以從這里得到mysql數(shù)據(jù)庫(kù):
http://www.mysql.com/
聯(lián)系客服