他のバージョンの文書 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

8.6. バイナリデータの格納

PostgreSQL は、バイナリデータの格納方法として2 つの別々の方法を用意しています。バイナリデータは PostgreSQL's のバイナリデータ型 bytea を使用してテーブル内に格納することも、ラージオブジェクト を使用して特殊な形式で別のテーブルに格納し、テーブル内に格納される OID 型の値で参照することもできます。

どちらの方法が適切かを決定するためには、それぞれの方法における制限を理解しなければなりません。bytea データ型は非常に巨大なバイナリデータを格納するのには適していません。bytea 型の列は 1 ギガまでのバイナリデータを保存できますが、そういった巨大な値を処理する時、非常に多量のメモリ( RAM)が必要になります。ラージオブジェクトによるバイナリデータの格納方法は、非常に巨大な値を格納するのに適していますが、こちらの場合も制限があります。特に、ラージオブジェクトを含む行を削除しても、ラージオブジェクトは削除されません。ラージオブジェクトを削除するためには、別途操作を行わなければなりません。ラージオブジェクトにはまた、セキュリティに関する問題がいくつかあります。データベースに接続した全てのユーザは、ラージオブジェクトを含む行を参照、変更する権限がなくても、全てのラージオブジェクトを参照、変更することができるからです。

7.2 は bytea データ型をサポートする JDBC ドライバの最初のリリースです。7.2 におけるこの機能の導入により、以前のリリースの動作と違いが発生しています。7.2 における getBytes()setBytes()getBinaryStream() および setBinaryStream() メソッドは bytea データ型に対して操作を行います。7.1 でのこれらのメソッドはラージオブジェクトに関連した OID データ型に対して操作を行います。 Connectioncompatible プロパティを 7.1 という値に設定することで、ドライバを古い 7.1 の動作に戻すことができます。

bytea データ型を使用するには、単に、 getBytes()setBytes()getBinaryStream()setBinaryStream() メソッドを使用して下さい。

ラージオブジェクト機能を使用するためには、 PostgreSQL JDBC ドライバで提供される LargeObject API を使用、または、 getBLOB()setBLOB() メソッドを使用して下さい。

Important: PostgreSQL では、SQL トランザクション内でラージオブジェクトをアクセスしなければなりません。入力パラメータとして false を指定した setAutoCommit() メソッドを使用して、トランザクションを開くことができます。

Note: 将来の JDBC ドライバでは、getBLOB()setBLOB() メソッドはラージオブジェクトと関係せず、 bytea データ型に対する操作になります。ですから、ラージオブジェクトを使用する予定ならば、LargeObject API を使用することを推奨します。

Example 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);

画像を挿入するには、以下のようにします。

// 全ての LargeObject API の呼び出しはトランザクション内部でなければなりません。
conn.setAutoCommit(false);

// 操作を実行するラージオブジェクトマネージャを入手します。
LargeObjectManager lobj = ((org.postgresql.Connection)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();

// imagesLO に行を挿入します。
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.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();