| PostgreSQL 7.4 文檔 | ||||
|---|---|---|---|---|
| Prev | Fast Backward | Chapter 31. JDBC 接口 | Fast Forward | Next |
PostgreSQL 提供兩種不同的方法存儲二進制數據。 二進制數據可以使用二進制數據類型 bytea存儲在表中,或者使用大對象 特性,該特性以一種特殊的格式將二進制數據存儲在一個獨立的表中, 然後通過在你的表中保存一個指向該表的類型為 oid 的數值來引用它。
為了判斷那種方法比較合適,你必須理解每種方法的局限。 bytea 數據類型並不適合存儲非常大數量的二進制數據。 雖然類型為 bytea 的字段可以存儲最多 1G 字節的二進制數據, 但是這樣它會要求數量巨大的內存來處理這樣巨大的數值。存儲二進制的大對象的方法更適合存儲非常大的數值, 但也有自己的局限。特別是刪除一個包含大對象引用的行並未刪除大對象。 刪除大對象是一個需要執行的獨立的操作。大對象還有一些安全性的問題, 因為任何聯接到數據庫的人都可以查看或者更改大對象,即使他們沒有查看/更新包含大對象引用的行的權限也一樣。
版本 7.2 是第一個支持 bytea 類型的 JDBC 驅動版本。在 7.2 中引入的這個功能同時也引入了一個和以往的版本不同的行為。自 7.2 以來,方法 getBytes(), setBytes(), getBinaryStream() 和 setBinaryStream() 操作 bytea 類型。 在 7.1 和更早的版本裡這些方法操作和 OID 類型關聯的大對象。 我們可以通過在 Connection 上設置 compatible 屬性為數值 7.1 來獲取舊的 7.1 的行為。
要使用 bytea 數據類型你只需要使用 getBytes(),setBytes(), getBinaryStream(),或者 setBinaryStream() 方法。
要使用大對象的功能,你可以使用 PostgreSQL JDBC 驅動提供的 LargeObject 類,或者使用 getBLOB() 和 setBLOB() 方法。
Important: 你必須在 一次 SQL 事務內訪問大對象。你可以通過 調用 setAutoCommit(false) 打開一個事務。
注意: 在將來的 JDBC 驅動中, getBLOB() 和 setBLOB() 方法可能不再操作大對象,而是將處理 bytea 數據類型。 因此如果你要用大對象,我們建議你使用 LargeObject API。
Example 31-8 包含一些有關如何用 PostgreSQL JDBC 驅動處理二進制數據的例子。
Example 31-8. 在 JDBC 裡處理二進制數據例子
比如,假設你有一個表包含一幅圖像和它的文件名, 並且你還想在 bytea 字段裡存儲圖像︰
CREATE TABLE images (imgname text, img bytea);
要插入一幅圖象,你可以:
File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);
PreparedStatement ps = conn.prepareStatement("INSERT INTO images VALUES (?, ?)");
ps.setString(1, file.getName());
ps.setBinaryStream(2, fis, file.length());
ps.executeUpdate();
ps.close();
fis.close();這裡,setBinaryStream() 把來自一個流的 一些數目的字節轉換成類型 bytea 的字段。 如果圖像的內容已經放在 byte[] 裡面了, 那麼你也可以用 setBytes() 方法幹這件事。
檢索一幅圖象甚至更容易(我在這裡使用PreparedStatement, 當然用Statement也是一樣的):
PreparedStatement ps = con.prepareStatement("SELECT img FROM images WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
if (rs != null) {
while (rs.next()) {
byte[] imgBytes = rs.getBytes(1);
// 從這開始使用數據
}
rs.close();
}
ps.close();這裡的二進制數據是以 byte[] 形式檢索的。你也可以使用一個 InputStream。
另外你可能會需要存儲一個非常大的文件,因此希望使用 LargeObject 類存儲該文件︰
CREATE TABLE imageslo (imgname text, imgOID oid);
要插入一個圖像,你可以用︰
// 所有大對象 API 調用都必須在一次事務中
conn.setAutoCommit(false);
// 獲取大對象管理器以便進行操作
LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();
//創建一個新的大對象
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
//打開一個大對象進行寫
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);
// 現在打開文件
File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);
// 從文件拷貝數據到大對象
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = fis.read(buf, 0, 2048)) > 0){
obj.write(buf, 0, s);
tl += s;
}
// 關閉大對象
obj.close();
//現在向 imgeslo 插入行
PreparedStatement ps = conn.prepareStatement("INSERT INTO imageslO VALUES (?,?)");
ps.setString(1, file.getName());
ps.setInt(2, oid);
ps.executeUpdate();
ps.close();
fis.close();
從大對象中檢索圖像︰
// 所有 LargeObject API 調用都必須在一個事務裡
conn.setAutoCommit(false);
// 獲取大對象管理器以便進行操作
LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();
PreparedStatement ps = con.prepareStatement("SELECT imgoid FROM imageslO WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
if (rs != null) {
while (rs.next()) {
//打開大對象讀
int oid = rs.getInt(1);
LargeObject obj = lobj.open(oid, LargeObjectManager.READ);
//讀取數據
byte buf[] = new byte[obj.size()];
obj.read(buf, 0, obj.size());
//在這裡對讀取的數據做些處理
// 關閉對象
obj.close();
}
rs.close();
}
ps.close();