PostgreSQL 提供两种不同的方法来存储二进制数据。二进制数据可以被存储在一个使用bytea类型的表中,或者把大对象用特殊格式的二进制存储在一个分离的参照表中,而在你的表中存储该参照表的oid值。
为了确定哪种方法比较适合,你需要理解每种方法的局限性。用bytea数据类型的方法不太适合于存储大量的二进制数据,虽然一个butea的属性可以存储高达1GB的二进制数据,但是它需要占用大量内存来完成这个大数据。大对象方法在存储非常大数据量的对象方面略胜一筹,但是它也有它的局限性。特别是在删除某行含有大对象参照的时候实际上并没有删除大对象,要删除大对象需要额外的动作去完成。大对象也有安全方面的问题,因为任何连接到数据库的人可以看到并/或者修改任何大对象,即使他们没有查看或更新含有大对象参照的行的权力。
7.2版本第一次使JDBC驱动程序支持bytea的数据类型,在7.2的功能介绍中曾介绍了与之前版本的性能比较。从7.2开始,方法getBytes(),setBytes(),getBinaryStream()以及setBinaryStream()用来操作bytea数据类型。在7.1及其以前版本,这些方法用来操作与大对象关联的oid数据类型。通过设置相容的恢复驱动程序到7.1以前版本来连接7.1以前的对象是有可能的。详见"连接参数"一章。
你可以仅使用getBytes(),setBytes(),getBinaryStream()以及setBinaryStream()方法来使用byte数据类型。
你可以用使用PostgreSQL的JDBC驱动程序提供的大对象类型或者使用getBLOB()和setBLO B()方法。
| |
Note
你必须在SQL事物块当中操作大对象,你可以使用函数setAutoCommit(false)启动事物块。
|
|
|
例7.1 "处理JDBC中的二进制数据"包含一些访问PostgreSQL的JDBC驱动程序的二进制数据的例子。
例7.1 处理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()方法也可以实现。
| |
Note
setBinaryStream的参数length是合适的。没有方法显示流的长度是不可知的。如果是这种情况,你必须亲自读取流到一个临时存储器中并计算长度,然后你才可以从临时存储器中发送正确的长度到驱动程序上。
|
|
|
先找到图象(我们这里采用PreparedStatement,但是Statement类也可以使用的)。
| |
PreparedStatement ps =conn.prepareStatement("SELECT img FROM images WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
byte[] imgBytes = rs.getBytes(1);
// 用一种方法表示数据
}
rs.close();
ps.close();
|
|
这里二进制数据被恢复为byte[],你可以用InputStream对象代替。你也可以选择用大对象API来存储存储大文件。
CREATE TABLE imageslo (imgname text, imgoid oid);
可以用以下方法插入图像:
| |
// 所有大对象API调用都必须在事物块内
conn.setAutoCommit(false);
// 取得Large Object Manager来操作
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;
}
// 关闭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();
// Finally, commit the transaction.
conn.commit();
|
|
从大对象中取回图片:
| |
// 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 = conn.prepareStatement("SELECT imgoid FROM imageslo WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
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();
// Finally, commit the transaction.
conn.commit();
|
|