考慮大對象:利用 JAVA 操作 DB2 Universal Database 中的 LOB 數據
2024-09-06 23:58:02
供稿:網友
簡介
諸如圖像和音頻這樣的大型數據對象經常需要存儲在數據庫中。db2 universal database 為存儲大型數據對象提供了專門的數據類型。這些數據類型被稱為大型對象(lob)。在本文中,我們將在 lob 的世界里遨游。更具體地說,我們將從 java 程序開發者的角度來研究 lob。您將看到如何在 java 應用程序中存儲以及檢索 lob。
業務場景
將您學到的東西應用到一個真實世界的例子總是有益的。因此,在本文中,我們將模擬一家書店。我們希望能夠為網站訪問者提供一種檢索書的封面圖像的方法。之后,您將看到我們怎樣將書的封面圖像存儲為 blob。此外,我們還希望將書的摘要以 clob 數據類型存儲在數據庫中以便通過關鍵字來搜索。下面的圖將使您了解在本文中我們將要完成的工作流。
圖 1
數據類型的 lob 家族
db2 提供了三種不同的大型對象數據類型。所有這些數據類型都能存儲最多達 2gb 的數據:
clob (character large object) - 能包含最多達 2gb 的字符數據。 blob (binary large object) - 能包含最多達 2gb 的二進制數據。這里的二進制數據實質上可以是任何東西(例如圖像,音頻文件 ,等等)。 dbclob (double-byte character large object) - 能包含最多達 2gb 的雙字節字符 數據。請注意只有當您所創建的數據庫支持雙字節數據時,才能使用這種數據類型。
這些數據類型均屬于 sql3 數據類型。正如您將在本文后面看到的,jdbc 2.0 支持這些數據類型。
除非不得已,否則不要移動它
在存儲器中移動大型對象是很占用資源的操作。因此,db2 提供了與 lob 交互的功能,該功能可以使這種移動減到最少。
數據在哪里?
注意到在 db2 中 lob 有一個重要且有趣的特點就是,lob 的值沒有存儲在數據庫的表中。實際上存儲的是描述符,該描述符指向 lob 的實際位置。真正的 lob 值是存儲在表空間里的,而表空間就是一些物理存儲單元。
表示 lob 值的定位器
實際上存儲在 lob 列中的不是真正的 lob 數據,而是一個指向 lob 數據的指針。該指針被稱為“定位器”。這些所謂的定位器用于表示 lob 值。當您在一個 resultset 中檢索數據時,您將檢索的是定位器而非這些定位器所表示的實際的 lob 值。正如您將看到的,您必須顯式地指出要檢索的 lob 值。用數據庫術語來說,這種檢索稱作“具體化(materialization)”。
開始
讓我們啟動 db2 command line processor 來做 lob 的實驗。
首先,我們創建名為 "lobdb" 的數據庫:
db2 => create database lobdb
使用用戶名 db2admin 和密碼 db2admin 來連接該數據庫:
db2 => connect to lobdb user db2admin using db2admin
出于學習的目的,我們將創建一對表,分別存儲一個 blob 和一個 clob:
db2 => create table bookcovers (bookisbn varchar(10) not null, bookcover blob (100k) not null, primary key(bookisbn))
db2 => create table abstracts (bookisbn varchar(10) not null, bookabstract clob (100k) not null, primary key(bookisbn))
上面語句中的 100k 是聲明我們需要存儲的 lob 的最大長度。此長度可以在 1b 到 2gb 中變化。當 lob 被具體化時,應用程序將通過這個最大長度信息分配一個大小合適的緩沖區來容納數據。
您可以使用下面的后綴來表示長度的字節數目:
k: 千字節 (1,024 個字節)
m: 兆字節 (1,048,576 個字節)
g: 千兆字節 (1,073,741,824 個字節)
在我們的兩個表中,書是通過主鍵 bookishn 來區分的。bookisbn 這列就是表示書的 isbn 編號。
是否壓縮?
創建一個表時,您可以指定 compact 或 not compact 選項。如果指定了 compact 選項,您所存儲的 lob 數據所占空間將最小。然而,如果您向 lob 列執行更新操作且該操作會增加該 lob 的大小,則會有性能損失。另一方面,如果您指定 not compact 選項(默認值),實質上您的 lob 值就可以有增長的余地。
是否作日志記錄?
如果您指定了 logged 選項,那么 lob 列上的更新都將被記錄在系統日志中。指定 logged 選項可以為存儲的數據提供最大保護。這樣就可以通過向前恢復過程來重建數據以防發生介質故障。然而這是以犧牲磁盤空間為代價的(更不用說寫到磁盤所付出的時間代價了)。如果您沒有指定一個選項,則默認選中 logged 選項。
插入 lob
通過 java 插入 blob 十分簡單。請看下面的代碼樣本:
preparedstatement preparedstatement = connection.preparestatement("insert into bookcovers values(?,?)");file imagefile = new file("c:////redbookcover.jpg");inputstream inputstream = new fileinputstream(imagefile);preparedstatement.setstring(1," 0738425826");preparedstatement.setbinarystream(2,inputstream,(int)(imagefile.length()));preparedstatement.executeupdate();
上面這個簡短的代碼片斷從 c 盤根目錄下取出名為 redbookcover.jpg 的文件并將其存儲到數據庫中。請注意我們是怎樣將文件與一個 inputstream 相關聯的。 此 inputstream 對象被用來填充針對 blob 列而存在的準備好的語句的值。此代碼和本文中的其他代碼都能夠在本文對應的項目 zip 文件中找到。
clob 的插入與以上所示 blob 的插入幾乎一模一樣。您可以參閱名為 clobinsertion.java 的項目文件。您將在此文件中看到我們是以 clob 的形式來存儲書的摘要的。而摘要是從名為 redbookabstract.txt 的文本文件中抓取的。
檢索 lob
我們已經插入了一些 lob。現在我們怎樣在 java 中檢索它們呢?這也是個很簡單的過程。
preparedstatement preparedstatement =connection.preparestatement("select bookcover from bookcovers where bookisbn=?");preparedstatement.setstring(1, "0738425826");resultset resultset = preparedstatement.executequery();while (resultset.next()) {// materialization of the blobblob blob = resultset.getblob(1);inputstream inputstream = blob.getbinarystream();file fileoutput = new file("c:////clonedredbookcover.jpg");fileoutputstream fo = new fileoutputstream(fileoutput);int c;while ((c = inputstream.read()) != -1)fo.write(c);fo.close();system.out.println("blob retrieved");
上面的代碼段中,我們執行了一個準備好的語句,我們在該語句中選擇了在前面代碼段中插入的那個 blob。需要注意的一個要點就是直到下面這行代碼之后,真正的 blob 才被具體化:
inputstream inputstream = blob.getbinarystream();
我們使用輸入流來將檢索到的 blob 存儲在一個名為 clonedredbookcover.jpg 的文件中。
檢索 clob 的語法大多跟檢索 blob 很相似。請參閱項目文件 clobretrieval.java。
jdbc api 提供了手段使我們可以對 clob 做一些有趣的操作。
請查看以下代碼:
clob clob = resultset.getclob(1);//retrieve the first 200 charactersstring substring = clob.getsubstring(1,200);system.out.println("first 200 characters of clob (book abstract): " + substring);// find the position of a given string in the cloblong positionofstring = clob.position("tool",1);system.out.println("position of substring: " + positionofstring);
在上面的代碼中,我們使用 getsubstring 方法來獲取存儲的 clob 的一部分。這樣的函數可以在僅僅需要書的摘要的部分內容的客戶應用程序中使用。上面的代碼還演示了 position 方法。如果一個給定的字符串包含在我們的 clob 中,該方法將返回一個 long 表示這個被搜索串在 clob 中的起始位置。如果該字符串沒有包含在 clob 中,將返回 -1。clob 接口提供的 position 方法不需要將存儲的數據具體化就可以運行。
clob 的實際具體化以如下代碼方式出現:
file fileoutput = new file("c:////clonedredbookabstract.txt");fileoutputstream fo = new fileoutputstream(fileoutput);inputstream is = clob.getasciistream();int c;while ((c = is.read()) != -1) fo.write(c);fo.close();
在上面的代碼片斷中,我們將存儲的 clob 具體化成一個名為 clonedredbookabstract.txt 的新文件。我們使用 clob 中的 getasciistream 方法來完成該操作。正如在檢索 blob 時所做的一樣,我們先將數據流分配給 inputstream ,然后從中讀取并將所讀取的內容寫入 fileoutputstream。
結束語
在本文中,您看到了 db2 udb 是如何提供工具來存儲大型數據對象的。使用 jdbc 2.0 api,您就可以通過 java 與 lob 交互。本文向您介紹了 jdbc api 的用法。我們是通過向您介紹一個真實世界的例子——一家書店需要存儲封面圖像和書的摘要來介紹其用法的。您現在應該可以在您自己的 java 應用程序中熟練使用 lob 了。
進階讀物
developing java applications using db2 image and audio extenders, part 1
http://www-106.ibm.com/developerworks/library/it/it-1001art29/
db2 universal database v8 application development - java application development
http://www-3.ibm.com/software/data/db2/udb/ad/v8/java/
使用 db2 版本 8 開發企業 java 應用程序
http://www.ibm.com/developerworks/cn/dmdd/library/techarticles/0209hutchison/index.shtml
advanced programming for the java platform - jdbc technology
http://developer.java.sun.com/developer/onlinetraining/programming/jdcbook/jdbc.html
致謝
感謝 robert indrigo 幫助我審核了本文。