32

jdbc シン ドライバーを使用して BLOB を Oracle データベースに挿入する方法について Web を検索すると、ほとんどの Web ページで 3 段階のアプローチが提案されています。

  1. 値を挿入しempty_blob()ます。
  2. で行を選択しますfor update
  3. 実際の値を挿入します。

これは私にとってはうまくいきます。例を次に示します。

Connection oracleConnection = ...

byte[] testArray = ...

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test (id, blobfield) values(?, empty_blob())");
ps.setInt(1, 100);
ps.executeUpdate();
ps.close();
ps = oracleConnection.prepareStatement(
    "select blobfield from test where id = ? for update");
ps.setInt(1, 100);
OracleResultSet rs = (OracleResultSet) ps.executeQuery();
if (rs.next()) {
    BLOB blob = (BLOB) rs.getBLOB(1);
    OutputStream outputStream = blob.setBinaryStream(0L);
    InputStream inputStream = new ByteArrayInputStream(testArray);
    byte[] buffer = new byte[blob.getBufferSize()];
    int byteread = 0;
    while ((byteread = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, byteread);
    }
    outputStream.close();
    inputStream.close();
}

作成者がより単純な 1 ステップのソリューションを使用することを提案している Web ページがいくつかあります。このソリューションの前の例:

Connection oracleConnection = ...

byte[] testArray = ...

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test(id, blobfield) values(?, ?)");
BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION);
OutputStream outputStream = blob.setBinaryStream(0L);
InputStream inputStream = new ByteArrayInputStream(testArray);
byte[] buffer = new byte[blob.getBufferSize()];
int byteread = 0;
while ((byteread = inputStream.read(buffer)) != -1) {
    outputStream.write(buffer, 0, byteread);
}
outputStream.close();
inputStream.close();

ps.setInt(1, 100);
ps.setBlob(2, blob);
ps.executeUpdate();
ps.close();

2 番目のコードははるかに簡単なので、私の質問は次のとおりです。最初の (一般的な) ソリューションのポイントは何ですか? 2 番目のソリューション (Oracle サーバーのバージョン番号、jdbc ドライバーのバージョン、BLOB のサイズなど) になんらかの制約がありますか? 最初の解決策は優れていますか (速度、メモリ消費量など)? より単純な 2 番目のアプローチを使用しない理由はありますか?

まったく同じ質問が CLOB フィールドにも当てはまります。

4

9 に答える 9

11

最初のケースで言及した更新アプローチは、純粋な JDBC コードを使用して書き直すことができるため、Oracle 固有のクラスへの依存を減らすことができます。これは、アプリがデータベースに依存しない必要がある場合に役立ちます。

public static void updateBlobColumn(Connection con, String table, String blobColumn, byte[] inputBytes, String idColumn, Long id) throws SQLException {
  PreparedStatement pStmt = null;
  ResultSet rs = null;
  try {
    String sql = 
      " SELECT " + blobColumn + 
      " FROM " + table + 
      " WHERE " + idColumn + " = ? " +
      " FOR UPDATE";
    pStmt = con.prepareStatement(sql, 
      ResultSet.TYPE_FORWARD_ONLY, 
      ResultSet.CONCUR_UPDATABLE);
    pStmt.setLong(1, id);
    rs = pStmt.executeQuery();
    if (rs.next()) {
      Blob blob = rs.getBlob(blobColumn);
      blob.truncate(0);
      blob.setBytes(1, inputBytes);
      rs.updateBlob(blobColumn, blob);
      rs.updateRow();
    }
  }
  finally {
    if(rs != null) rs.close();
    if(pStmt != null) pStmt.close();
  }
}

MSSQL の場合、ロック構文が異なることを理解しています。

String sql = 
  " SELECT " + blobColumn + 
  " FROM " + table + " WITH (rowlock, updlock) " + 
  " WHERE " + idColumn + " = ? "
于 2009-05-14T13:15:18.660 に答える
7

OracleDBAからのもう1つの視点。Sunの連中は、JDBC標準(1.0、2.0、3.0、4.0)を設計したとき、非常に貧弱な仕事をしていました。BLOBはラージオブジェクトを表すため、非常に大きくなる可能性があります。これは、JVMヒープに格納できないものです。Oracleは、BLOBをファイルハンドルのようなものと見なします(実際には、BLOBは「lobロケーター」と呼ばれます)。LOBSはコンストラクターを介して作成することはできず、Javaオブジェクトではありません。また、LOBロケーター(oracle.sql.BLOB)は、コンストラクターを介して作成することはできません。DB側で作成する必要があります。Oracleでは、LOBを作成する方法が2つあります。

  1. DBMS_LOB.CREATETEMPORATY-この場合に返されるロケーターは、一時表領域を指します。このロケーターに対するすべての書き込み/読み取りは、ネットワーク経由でDBサーバーに送信されます。JVMヒープには何も保存されません。

  2. EMPTY_BLOB関数を呼び出します。INSERT INTO T1(NAME、FILE)VALUES( "a.avi"、EMPTY_BLOB())RETURNING FILE INTO ?; この場合、返されたlobロケーターポイントはデータテーブルスペースにあります。このロケーターに対するすべての書き込み/読み取りは、ネットワーク経由でDBサーバーに送信されます。すべての書き込みは、REDOログへの書き込みによって「保護」されます。JVMヒープには何も保存されません。return句はJDBC標準(1.0、2.0)でサポートされていなかったため、インターネット上で、「INSERT ...; SELECT ...FORUPDATE;」という2つのステップのアプローチを推奨する多くの例を見つけることができます。

Oracle lobは、何らかのデータベース接続に関連付ける必要があります。DB接続が失われた/閉じられた/(または「コミットされた」)場合は使用できません。ある接続から別の接続に渡すことはできません。

2番目の例は機能しますが、一時表領域からデータ表領域へのデータの場合は過剰なコピーが必要になります。

于 2012-03-15T15:20:04.273 に答える
5

Oracle サーバーの LOB 処理はかなり貧弱であり、重大なパフォーマンスの問題 (例: REDO ログの大量の過剰使用) に悩まされる可能性があるため、最初の解決策はそれらに対処する方法かもしれません。

両方のアプローチを試すことをお勧めします。有能な DBA がいる場合は、サーバーへの影響が最も少ないアプローチをアドバイスできる場合があります。

于 2009-05-14T09:59:11.113 に答える
4

この文 :

blob.setBytes(1, inputBytes);

Oracle シン クライアント ojdbc14.jar、「サポートされていない機能」を使用すると問題が発生する

したがって、次の方法で回避する必要がありました。

rset.next();
Blob bobj = rset.getBlob(1);
BLOB object = (BLOB) bobj;
int chunkSize = object.getChunkSize();
byte[] binaryBuffer = new byte[chunkSize];
int position = 1;
int bytesRead = 0;
int bytesWritten = 0, totbytesRead = 0, totbytesWritten = 0;
InputStream is = fileItem.getInputStream();
while ((bytesRead = is.read(binaryBuffer)) != -1) {
bytesWritten = object.putBytes(position, binaryBuffer, bytesRead);
position += bytesRead;
totbytesRead += bytesRead;
totbytesWritten += bytesWritten;
is.close();
于 2010-08-17T21:36:36.037 に答える
2

2番目の解決策のためにいくつかの注意点が見つかりました

私はojdbc6.jarを使用しています-最新リリースと「2番目のソリューション」のステートメント:

BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION);

ステートメントが完了した後にblobを解放する必要があります。そうしないと、セッションが閉じられたときにblobが閉じられます(接続プールでは時間がかかる場合があります)。

blob.freeTemporary();

それ以外の場合は、ロックされたリソースが表示されます。

select * from v$temporary_lobs

一時BLOBのもう1つの問題は、一時テーブルスペースを割り当てる必要があることです。ドキュメントhttp://docs.oracle.com/cd/E11882_01/appdev.112/e18294.pdf

一時LOBの一時表スペースの管理一時テー​​ブルスペースは、一時LOBデータを保管するために使用されます。

于 2012-10-01T13:29:54.633 に答える
2

CLOB データが十分に小さいため、爆発することなくメモリに収まる場合は、準備済みステートメントを作成して呼び出すだけで済みます。

ps.setString(1, yourString);

他のサイズ制限があるかもしれませんが、私たちが扱っているサイズ (最大 500kB) ではうまくいくようです。

于 2011-07-04T22:10:10.627 に答える
1

setObject(pos, byte[])私のケースで動作する簡単な呼び出しを見つけました。「JDBC と Java を使用したデータベース プログラミング」より、George Reese 著

        byte[] data = null;
        stmt = con.prepareStatement("INSERT INTO BlobTest(fileName, "
            + "blobData) VALUES(?, ?)");
        stmt.setString(1, "some-file.txt");
        stmt.setObject(2, data, Types.BLOB);
        stmt.executeUpdate();
于 2012-06-25T17:12:53.160 に答える
0

挿入する BLOB のサイズが blob.getBufferSize() より大きい場合jdbc 接続の autoCommit プロパティのデフォルト値が true であるため、最初のチャンクが db に書き込まれるとすぐにトランザクションがコミットされ、db がそれらを新しいトランザクションとして扱うため、それ以降のチャンクの書き込みは失敗します。
a) jdbc 接続の autoCommit プロパティを false に設定します。

conn.setAutoCommit(false);

b) BLOB 全体をアップロードした後、トランザクションを明示的にコミットします。

while ((bytesRead = messageInputStream.read(buffer)) != -1) {
     cumBytes += bytesRead;
     blobOutputStream.write(buffer, 0, bytesRead);
    }
conn.commit();
于 2016-08-20T06:08:08.247 に答える