pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL 提供两种不同的 存储二进制数据的方式。二进制数据可以使用 bytea 数据类型存储在一个表中,或者使用大对象 特性——它把二进制数据以一种特殊格式存储在一个单独的 表中,并通过在你的表中存储一个 oid 类型的值来引用该表。
为了确定哪种方法合适,你 需要理解每种方法的限制。 bytea 数据类型并不适合存储非常 大量的二进制数据。虽然 bytea 类型的列最多可以容纳 1 GB 的二进制数据,但处理 这样大的值需要 大量的内存。用大对象方法 存储二进制数据更适合存储非常大的值, 但它也有自己的限制。具体来说,删除一个 包含大对象引用的行并不会删除该大对象。 删除大对象是一个需要单独执行的 操作。大对象还有一些安全性 问题,因为任何连接到数据库的人都可以查看 和/或修改任何大对象,即使他们没有 查看/更新包含该大对象引用的行的权限。
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() 方法。
你必须在一个 SQL 事务块内访问大对象。你可以通过调用 setAutoCommit(false) 来启动一个事务块。
在 JDBC 驱动的一个未来版本中, getBLOB() 和 setBLOB() 方法可能不再 与大对象交互,而是改为处理 bytea 数据类型。因此,如果你打算使用大对象,建议 使用 LargeObject API。
例 31.8 包含一些关于 如何用 PostgreSQL JDBC 驱动处理二进制数据的例子。
例 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);
// use the data 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 block
conn.setAutoCommit(false);
// Get the Large Object Manager to perform operations with
LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();
// Create a new large object
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
// Open the large object for writing
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 block
conn.setAutoCommit(false);
// Get the Large Object Manager to perform operations with
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()) {
// 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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。