2010年1月31日日曜日

【Oracle】JDBC経由でBLOBデータ挿入

BLOB型のカラムにJDBC経由でデータを挿入する方法。
Oracle11gのマニュアルには、BLOBよりも直接バインディングを使うとラウンドトリップを削減できるとある。
また、直接バインディングではバッチ実行を使った場合でも、1回のネットワーク操作で送信されるとあるので、もはやBLOBオブジェクトって使うことないのではないかと。(Integer#MAX_VALUEを超えるデータ量を、設定する場合はBLOBを使うしかないけど、そんなデータ量になることなんてまれな気もするし。)

直接バインディングの実装例
この実装でも、バインドされる値が4000バイトを超えると(4001バイト以上)ストリームバインディングになるらしい。
PreparedStatement statement = con.prepareStatement(
        "insert into blob_test (ID, BLOB_DATA) values (?, ?)");
try {
    statement.setString(1, "01");
    // BLOBに10進数の「12345」を設定
    statement.setBytes(2, new byte[] {0x31, 0x32, 0x33, 0x34, 0x35});
    statement.executeUpdate();
} finally {
    statement.close();
}

実行結果
insert文でバインドした値(0x31, 0x32, 0x33, 0x34, 0x35)が挿入されている。





ファイルなどのデータサイズが大きい物を登録する方法。
ファイルの場合であれば、ヒープ上にファイルの中身を読み込んでからバインドするのではなくInputStreamをバインドしてあげればよい。
この場合は、データサイズが4000バイトを超えるのでストリームバインディングになるらしい。またラウンドトリップが複数回になるので、それなりに性能面に与える影響があると思う。
PreparedStatement statement = con.prepareStatement(
        "insert into blob_test (ID, BLOB_DATA) values (?, ?)");
FileInputStream fis = null;
try {

    statement.setString(1, "02");
    // BLOBにFileInputStreamを設定
    File file = new File("C:\\work\\hoge.pdf");
    fis = new FileInputStream(file);
    statement.setBinaryStream(2, fis, (int) file.length());
    statement.executeUpdate();
} catch (FileNotFoundException e) {
    throw new RuntimeException("file io error.", e);
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            // nop
        }
    }
    statement.close();
}

実行結果
でかめのPDFをinsertしてみたが、ちゃんとDBに登録されている。