pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL 提供两种不同的 存储二进制数据的方式。二进制数据可以使用 PostgreSQL 的二进制数据类型 bytea 存储在一个表中,或者使用大对象 特性——它把二进制数据以一种特殊格式存储在一个单独的 表中,并通过在你的表中存储一个 OID 类型的值来引用该表。
为了确定哪种方法合适,你 需要理解每种方法的限制。 bytea 数据类型并不适合存储非常 大量的二进制数据。虽然 bytea 类型的列最多可以容纳 1Gig 的二进制数据,但处理 这样大的值需要 大量的内存(RAM)。用大对象方法 存储二进制数据更适合存储非常大的值, 但它也有自己的限制。具体来说,删除一个 包含大对象的行并不会删除该大对象。 删除大对象是一个需要单独执行的 操作。大对象还有一些安全性 问题,因为任何连接到数据库的人都可以查看 和/或修改任何大对象,即使他们没有 查看/更新包含该大对象的行的权限。
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 API,或者使用 getBLOB() 和 setBLOB() 方法。
对于 PostgreSQL,你必须在一个 SQL 事务内访问大对象。你可以通过以 false 作为 输入参数调用 setAutoCommit() 方法来 打开一个事务。
在 JDBC 驱动的一个未来版本中, getBLOB() 和 setBLOB() 方法可能不再 与大对象交互,而是改为处理 bytea 数据类型。因此,如果你打算使用大对象,建议 使用 LargeObject API。
例 8.4. 二进制数据示例
例如,假设你有一个表存储一幅图像的文件名, 并且你还想把图像本身存储在一个 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);
// use the stream in some way here
}
rs.close();
}
ps.close();
这里二进制数据是作为 byte[] 检索的。你也可以 改用一个 InputStream 对象。
又或者,你要存储一个非常大的文件,并想使用 LargeObject API 来 存储该文件:
CREATE TABLE imagesLO (imgname text, imgOID OID);
要插入一幅图像,你可以使用:
// All LargeObject API calls must be within a transaction
conn.setAutoCommit(false);
// Get the Large Object Manager to perform operations with
LargeObjectManager lobj = ((org.postgresql.Connection)conn).getLargeObjectAPI();
//create a new large object
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
//open the large object for write
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);
// Now open the file
File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);
// Copy the data from the file to the large object
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = fis.read(buf, 0, 2048)) > 0)
{
obj.write(buf, 0, s);
tl += s;
}
// Close the large object
obj.close();
//Now insert the row into imagesLO
PreparedStatement ps = conn.prepareStatement("INSERT INTO imagesLO VALUES (?, ?)");
ps.setString(1, file.getName());
ps.setInt(2, oid);
ps.executeUpdate();
ps.close();
fis.close();
从大对象中检索该图像:
// All LargeObject API calls must be within a transaction
conn.setAutoCommit(false);
// Get the Large Object Manager to perform operations with
LargeObjectManager lobj = ((org.postgresql.Connection)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()) {
//open the large object for reading
int oid = rs.getInt(1);
LargeObject obj = lobj.open(oid, LargeObjectManager.READ);
//read the data
byte buf[] = new byte[obj.size()];
obj.read(buf, 0, obj.size());
//do something with the data read here
// Close the object
obj.close();
}
rs.close();
}
ps.close();
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。