Wolfgang Meier 的開放源碼的 eXist 數據庫可能是當今最流行的原生 XML 數據庫(但這并不是說它是最好的)。eXist 是用 Java? 編程語言編寫的,可運行在大多數主要平臺上。程序通過 eXist 綁定的 HTTP 服務器與 eXist 交互。SOAP、XML-RPC 和 RESTful 接口它都提供了,您可以通過這些接口向核心服務器提交 XPath、XQuery 和 XUpdate 請求。命令行和 GUI 客戶機也是可用的。
安裝 eXist
eXist 需要 Java 1.4 或更高版本,否則,所有必需的依賴關系都將被綁定。事實上,對于服務器端開放源碼項目來說,安裝 eXist 已經是相當容易了。其他很多項目,不管是開放源碼的,還是非開放源碼的,安裝時都可以從安裝 eXist 受到啟發。安裝程序是用 IzPack 構建的。發行版是一個 JAR 檔案文件。要安裝 eXist,只要像下面這樣運行該檔案文件即可:
$ java -jar eXist-1.0b2-build-1107.jar
|
安裝程序打開一個 GUI,詢問您要將 eXist 目錄安裝在哪里。我把它安裝在 /home/elharo/eXist 中。eXist/bin 目錄包含必需的啟動腳本。要啟動服務器,可執行 startup.sh (UNIX?) 或 startup.bat (Microsoft? Windows?):
該命令在端口 8080 上運行服務器,并開始服務 /eXist 中的文件。您可以從任何 Web 瀏覽器連接到 eXist。例如,我將 eXist 安裝在 eliza.elharo.com 上,所以我可以在以下 URL 處連接到 eXist:
http://eliza.elharo.com:8080/exist/
|
(您不要在自己家里這么去嘗試,因為我的防火墻會擋住您。您必須連接到您自己的服務器。)
最初,您將看到 eXist 文檔,還有一些您將要去探明的示例。
將數據裝載到 eXist 中
eXist 不是真正的 Web 服務器,它只是使用一個 Web 服務器作為到底層數據庫服務器的方便的接口。軟件包中還包含獨立的 GUI 客戶機和編程 API,您可以使用這些來執行各種操作。您甚至可以使用 WebDAV 從 Microsoft Windows Explorer 瀏覽 eXist。對于初次體驗來說,可能使用簡單的 GUI 客戶機是最容易的。要啟動客戶機,可從 eXist/bin 目錄執行 client.sh (UNIX) 或 client.bat (Windows):
從圖 1 可以看到,默認情況下,客戶機試圖連接到運行在端口 8080 上的本地主機上的 eXist 數據庫。您可以在 URL text 字段指定另外的主機和端口。這一個窗口也會要求輸入用戶名和密碼。默認情況下,用戶名是 admin,可以將 password 字段保持為空。
圖 1. 連接到 eXist
|
是的,您可以管理用戶、設置密碼、設置特權、將用戶分配到具有不同訪問權限的組,還可以完成完全生產環境中所有其他必需的任務。然而限于篇幅,許多這樣的選項都沒法一一介紹,而要將重點放在數據庫上。
|
|
您登錄之后,客戶機顯示圖 2 所示的 GUI。最初,eXist 帶有一個集合,叫做 system,其中存儲有用戶信息。現在您不想使用這個集合,而是通過選擇 File > New Collection 為文檔創建一個新的集合。我創建了一個名為 books 的集合。要打開集合,可在 GUI 中雙擊它。打開一個集合后,要上載文檔,可單擊看起來有點像一張彎曲的紙、旁邊有一個加號的圖標。
圖 2. eXist 管理客戶機
我首先上載兩個小文檔,數據庫毫無意見地接受它們。然后我嘗試上載我的 Processing XML with Java 一書的完整文本。這個操作悄無聲息地失敗了,沒有給出任何錯誤消息。不通過 GUI 客戶機上載,改為通過 Web 接口上載也失敗了。但是該接口給出了一個堆棧跟蹤,有助于調試問題。這表明 eXist 沒有解析文檔類型聲明中使用的相對 URL。要裝載具有外部 DTD 子集的文檔,您必須手動在服務器的文件系統上安裝 DTD,并編輯一個編目文件,告訴數據庫要裝載的文檔在哪里;然后,必須重啟數據庫服務器,使它重新裝載編目文件。這是一個主要的爭論點,盡管每個不同的 DTD 您通常只需要安裝一次。在您的文檔不使用 DTD 或者只使用少量不怎么改變的 DTD 時,eXist 工作得最好。
查詢 eXist
eXist 支持 XPath 和 XQuery(關于二者的更多信息,請參見 參考資料)。eXist 使用 2003 年 11 月 XQuery 工作草案中的 XQuery 語法。現在正在努力將數據庫更新到使用更多近期工作草案中的語法。對于基本的 For-Let-Where-Order-Return (FLWOR) 查詢來說,草案之間的差別并不大。
要輸入針對集合的查詢,單擊 GUI 客戶機中的望遠鏡圖標以顯示圖 3 所示的窗口。
圖 3. eXist 查詢窗口
煩人的是,復制和粘貼在該界面中不起作用,所以必須手動地鍵入所有查詢。當然,這個程序只是用于測試和體驗,不能用它來與諸如 Oracle 這樣的您必須在其中鍵入原始 SQL 的數據庫進行頻繁的交互。在對想要運行的查詢有了相當好的主意之后,您就可以編寫程序根據算法來生成和提交查詢,這一點我下面就會討論到。
編寫與 eXist 交互的程序
IBM?、Oracle 和 JSR 225 專家組的其他成員當前正在定義一個用于取代 XQuery 的 API,其中 XQuery 由 JDBC 用于取代 SQL。但是直到這個過程結束并在 eXist 中實現該 API,仍然有必要使用 eXist 的本機 API。您可以通過 SOAP、XML-RPC、WebDAV 或 HTTP 接口訪問這個 API。任何支持這些協議之一的 API 都可以與 eXist 通信。例如,可以使用 JAX-RPC、通過 SOAP 與 eXist 通信,或者使用 java.net、通過 HTTP 與它通信。
RESTful HTTP 接口是最簡單的,并且是這些選項中最廣泛可用的。例如,假設您想要找到包含單詞 “XSLT” 的 books 集合中的所有 para
元素。清單 1 中的 XQuery 找到所有這樣的元素。
清單 1. 一個示例 XQuery
for $p in //para
where contains($p, "XSLT")
return $p
|
您從下面這個 URL 獲得(GET
)該查詢:
http://eliza.elharo.com:8080/exist/servlet/db/books/
|
其中 eliza.elharo.com
是網絡主機,數據庫就運行在它上面;8080
是端口;/exist/servlet/db
分別識別 Web 應用程序、servlet 和數據庫;books
是您在該數據庫查詢的特定集合。eXist 允許嵌套的集合。例如,books 集合可能包含單獨的小說和非小說集合,這些集合可在下面這些 URL 處得到:
http://eliza.elharo.com:8080/exist/servlet/db/books/fiction/
http://eliza.elharo.com:8080/exist/servlet/db/books/nonfiction/
|
然而對本文來說,您想要查詢所有的書籍,包括小說和非小說。XQuery 被作為 URL 的查詢字符串(URL 中引號后的部分)中的 _query
字段的值發送。它必須以通常方式用百分比編碼(例如,空格成了 %20
,雙引號成了 %22
,等等)。因此,您可以通過獲得(GET
)下面這個 URL 將清單 1 中的查詢發送到服務器:
http://eliza.elharo.com:8080/exist/servlet/db/books/?_query=
for%20$p%20in%20//para%20where%20contains($p,%20%XSLT%22)%20return%20$p
|
服務器將查詢結果封裝在 exist:result
元素中之后發送回來,如清單 2 所示。
清單 2. 示例查詢的結果
<exist:result xmlns:exist="http://exist.sourceforge.net/NS/exist"
exist:hits="148" exist:start="1" exist:count="10">
<para><quote>HTML? You must be joking</quote> said the fourth, a computer
science professor on sabbatical from MIT, who was engrossed in an XSLT
stylesheet ...</para>
<para>XSLT and the TrAX API</para>
<para>Combine functional XSLT transforms with traditional imperative Java code</para>
<para>The TrAX API for XSLT processing</para>
<para>Once you′re comfortable with one or more of these APIs, you
can read Chapters 16 and 17 on XPath and XSLT.
However, those APIs and chapters do require some knowledge of at least one
of the three major APIs.</para>
...</exist:result>
|
其他可選查詢字符串變量控制結果是否是良好打印格式的、用什么元素封裝結果、返回多少匹配的值(默認情況下,eXist 只返回前 10 個匹配的值),等等。
由于這全是通過 HTTP GET
完成的,所以您可以簡單地通過在 Web 瀏覽器中鍵入適當的 URL 而執行該查詢。當然,任何通過 HTTP 傳遞信息的軟件庫也可以發送該查詢并取回 XML 流形式的結果。若要用 Java 語言來編寫該查詢,您可以使用 URLEncoder
類來編碼查詢字符串,用 URL
來提交查詢,并用 XOM 來處理結果,如清單 3 所示。
清單 3. 用 Java 代碼查詢 eXist
String xquery = "for $p in //para"
+ " where contains($p, "XSLT") "
+ " return $p";
String encodedQuery = URLEncoder.encode(xquery);
URL u = new URL("http://eliza.elharo.com:8080/exist/servlet/db/books/?_query=");
+ encodedQuery);
InputStream in = u.openStream();
Document doc = (new Builder()).build(in);
// work with the document...
|
像這樣的 HTTP 接口是完全語言獨立的。您可以容易地用 Perl、Python、C、C# 或任何其他具有一個簡單 HTTP 庫和一些 XML 支持的語言重新產生清單 3 中的功能。查詢這樣的數據庫最有效的一種方式是編寫一個 XSLT 樣式表來格式化結果。
插入文檔
XQuery 允許您從數據庫取出信息。但是放入數據呢?這甚至更加容易。不是發送 GET
請求,而是發送 PUT
請求。放入(PUT
)數據到其中的 URL 就是文檔將要放入數據庫中的 URL;請求的主體是將要存儲的文檔。例如,清單 4 中的 Java 代碼從 Cafe con Leche Web 站點取得 RSS 提要,并將它放入名為 20050401 的聯合集合中。
清單 4. 用 Java 代碼將文檔插入 eXist 中
URL u = "http://www.cafeaulait.org/today.rss";
InputStream in = u.openStream();
URL u = new URL("http://eliza.elharo.com:8080/exist/servlet/db/syndication/20050401");
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("PUT");
conn.setHeaderField("Content-type", "application/xml");
OutputStream out = conn.getOutputStream();
for (int c = in.read(); c != -1; c = in.read()) {
out.write(c);
}
out.flush();
out.close();
in.close();
// read the response...
|
將新文檔放入數據庫中(PUT
)通常需要認證。eXist 的 REST 接口支持 HTTP Basic 認證。Java 語言通過 java.net.Authenticator
類支持這種認證。本文不做詳細討論,但是簡要來說,您必須用一個知道(或者知道如何詢問)數據庫用戶名和密碼的類來繼承 Authenticator
,然后安裝該子類的的一個實例作為系統默認的認證程序(authenticator)。
刪除文檔
需要從集合中刪除文檔嗎?只要發送一個 DELETE
請求到適當的 URL 即可,如清單 5 所示。
清單 5. 刪除 eXist 中的一個文檔
URL u = new URL("http://eliza.elharo.com:8080/exist/servlet/db/syndication/20050401");
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setRequestMethod("DELETE");
conn.connect();
// read the response...
|
同樣,在實踐中也需要通過一個 Authenticator
對象提供用戶名和密碼。
更新文檔
最后也是最復雜的操作是修改數據庫中的信息。例如,假設我要將 e-mail 地址從 elharo@metalab.unc.edu 更改為 elharo@macfaq.com。因此,我想要將所有的 <email>elharo@metalab.unc.edu</email>
元素更改為 <email>elharo@macfaq.com</email>
。XQuery 不提供這種能力,所以 eXist 使用 XUpdate 來代替。清單 6 中的 XUpdate 查詢作出了更改。
清單 6. 使用 XUpdate 來更新 eXist 中的文檔
<xupdate:update
xmlns:xupdate="http://www.xmldb.org/xupdate"
select="http://email[.=′elharo@metalab.unc.edu′]">
elharo@macfaq.com
</xupdate:update>
|
由于該操作改變了一個資源,所以您需要使用 POST
方法來將它發送到服務器。您發出想要更改的文檔的 URL 并在請求體中給出 XUpdate 指令。
我只介紹了 REST 接口的要點。它也包含以下用途的指令:創建和刪除集合、指定查詢結果如何被格式化,以及提供用戶憑證。HTTP 也不只是 eXist 的接口。eXist 還具有針對 Perl、PHP 和 Java 語言的本機 API,以及一般的 WebDAV、SOAP 和 XML-RPC 接口。廣泛的 API 支持是 eXist 一個特別的優點。
性能、健壯性和穩定性
eXist 并不是世界上最快的數據庫。您可以容易地使用一個秒表來度量它裝載一個中等大小的文檔所需的時間,即使在連接到本地數據庫的快速硬件上也要花很長的時間。查詢速度類似于質量。在比較大的集合上進行復雜的查詢讓您有時間去煮一杯咖啡。為了縮短文檔裝載和查詢時間,您可以給 eXist 更多的內存。與 eXist 一起發布的默認配置指定的設置適合于具有 256 MB 左右內存的機器。如果具有一臺比較強大的服務器,您可以修改 conf.xml file 給 eXist 分配更多的內存。
為了優化數據庫,您可以添加索引。默認情況下,eXist 對元素和屬性節點以及文檔的全部文本做索引。您可以為可能出現在您的查詢中的特定節點集指定附加的范圍索引。例如,如果您知道自己可能執行大量的查找 para
元素的查詢,那么可以在 //para
上定義一個查詢。這告訴 eXist 預先執行和存儲文檔中所有 para
元素的值,因為它們很可能后面會用到。
因此,eXist 主要適合于速度不是很重要的小型集合。如果您具有千兆字節大小的文檔或者每小時要處理數千個事務,那么請尋求別的辦法。
同樣,我不能保證我的數據對 eXist 有效。我自己沒有體驗過數據破壞。但是其他開發人員會經常遇到并修復數據庫破壞問題。從正面來說,eXist 使得備份數據庫非常容易。非常重要的是,備份格式以真正的文本 XML 保存內容,而不是以專有的二進制格式保存;這意味著哪怕出現問題,您可以用文本編輯器來修復。如果您經常做歸檔備份,那么 eXist 的數據就不可能變得不可檢索。
eXist 功能恰當,能滿足一些基本的需求,并包含一些意想不到的附帶特性,比如 XInclude 支持。事務、回滾、撤退以及一些類似的企業級特性都沒有包括(事務列在 “即將實現” 的清單內);但是許多應用程序并不需要這類高級功能。
我對 eXist(就這一點來說,或者是任何其他基于 XQuery 的原生 XML 數據庫)最關心的一點是底層標準和 API 的穩定性。本文基于 eXist 的最新 beta 版,該版本基于 2003 年 11 月的 XQuery 草案、于 2004 年 11 月發布。eXist 的這個版本現在 CVS 中,作了一些向后不兼容的更改,這些更改還沒有完全文檔化。將來還會作更多的更改,不論是 eXist 中,還是它所依賴的 W3C 規范都會發生更改。除非您適應那些需要您重新測試和編寫代碼的頻繁更改,否則不要將 eXist 用于生產環境。
結束語
擁有的數據越多,越需要使用數據庫系統來管理數據。如果數據是 XML,那么穩定的原生 XML 數據庫無疑是適當的選擇。eXist 是一種穩定的系統嗎?很遺憾,答案是否定的。eXist 是一個有趣的研究項目,也許會在一兩年內開發成一款有用的工具。但是就其當前狀態來說,我無法推薦您使用它。文檔是不完整的,并且經常會引起誤解。沒有錯誤消息(各地的程序員請注意,異常堆棧跟蹤不能當作真正的錯誤消息,有時,eXist 甚至連堆棧跟蹤都沒有提供給您)。GUI 時常會違反用戶界面標準。像復制和粘貼這樣的基本特性被忽略了。在為本文做非常基本的測試期間,我遇到了多個 bug。
eXist 還沒有最終完成,當前還處于 beta 版本。在版本 1.0 發布之前,我遇到的許多問題都有望被解決,但不可能在短期內解決。我知道現在就有一些人將 eXist 用于實際的工作,這讓我有些想不通。要么就是他們非常幸運,要么就是他們仔細地編寫查詢和文檔,避免了 eXist 的 bug。如果您有興趣為一個有價值的開放源碼項目作貢獻,那么 eXist 就是一個值得您去貢獻的項目。但是使得它對于程序員來說是一個試探性項目的不完全性也使得它不適合于生產系統。
參考資料
凡是有該標志的文章,都是該blog博主Caoer(草兒)原創,凡是索引、收藏
、轉載請注明來處和原文作者。非常感謝。