[Oracle] LOBデータをファイルから直接登録する方法(その1) | Archive Redo Blog

Archive Redo Blog

DBエンジニアのあれこれ備忘録

LOBデータをOracleに登録する際には通常はDBMS_LOBパッケージのWRITEプロシージャ、またはJavaなどのプログラミング言語を用います。

ただし、これらの方法でLOBデータを登録する場合、登録するLOBデータを処理するプログラム(メモリー)上に読み込んでおかなければなりません。


例えば、サーバーサイドJavaの場合はアプリケーションサーバーのメモリー上に展開することになります。


そのため、サイズの大きなLOBデータを登録しようとすると、アプリケーションサーバーに大きな負荷をかけることになってしまいます。

LOBデータのソースがファイルである場合は、上記のような問題を解決するためにファイルから直接登録するという方法を用いることができます。

Oracleでは以下のような方法によってLOBデータをファイルから直接データベースに登録することができます。

※ここではBLOBデータを例に説明します。

DBMS_LOBパッケージのLOADFROMFILEプロシージャを使用する方法
DBMS_LOBパッケージでBLOBデータをファイルから直接登録する場合、まず、ファイルにアクセスできるようにするためにディレクトリを定義し、そのディレクトリにアクセスする権限を与える必要があります。

CREATE DIRECTORY FILE_DIR AS 'C:\Temp'
/
GRANT READ ON DIRECTORY FILE_DIR TO PUBLIC
/

そして以下のような手順でLOBデータをファイルから直接登録します。

DECLARE
  wk_blob        BLOB;
  wk_bfile      BFILE;
BEGIN
  --空のBLOBデータを登録
  INSERT INTO TABLEA ( COL1, COL2, COL3 )
    VALUES ( 1, 'TEST.DAT', empty_blob() );
  --BLOBロケータ取得
  SELECT COL3 INTO wk_blob
    FROM TABLEA
    WHERE COL1 = 1
    FOR UPDATE;
  -BFILEロケータを取得
  wk_bfile := BFILENAME('FILE_DIR', 'TEST.DAT');
  --ファイルをオープン
  DBMS_LOB.FILEOPEN( wk_bfile, DBMS_LOB.FILE_READONLY );
  --ファイルをBLOBロケータにロード
  DBMS_LOB.LOADFROMFILE( wk_blob, wk_bfile, DBMS_LOB.GETLENGTH( wk_bfile ) );
  --ファイルをクローズ
  DBMS_LOB.FILECLOSE(wk_bfile);
END;
/


まず、空のBLOBデータを登録し、次にFOR UPDATE句を指定して登録した空のBLOBデータのBLOBロケータを取得し、次にBFILENAMEファンクションを使って事前に定義したディレクトリと登録元のファイルを指定してBFLIEロケータを取得し、そしてDBMS_LOBパッケージのLOADFROMFILEプロシージャを使ってBFILEをBLOBロケータにロードするという手順です。


BFILEを扱うところ以外は基本的なLOB操作と特に変わりはありません。

Javaストアドプロシージャを利用する方法
JavaストアドプロシージャでBLOBデータをファイルから直接登録する場合、まず、BLOBデータをファイルから読み込んで登録するJavaのメソッドを持つクラスを用意します。

例えば以下のようなJavaクラスです。

package orcl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import oracle.sql.BLOB;

public class UtlLob {

  public static long loadBlob( BLOB blob, String dirName, String fileName ) {
    
    File dir = null;
    File file = null;
    FileInputStream fis = null;
    OutputStream os = null;

    try {

      //ディレクトリのFileオブジェクトを生成します。
      dir = new File( dirName );

      //ディレクトリが存在しない場合、終了します。(失敗)
      if( !dir.exists() ) {
        return -1L;
      }

      //読み込みファイルのFileオブジェクトを生成します。
      file = new File( dir, fileName );
      //読み込みファイルからの入力ストリームを生成します。
      fis = new FileInputStream( file );
      //BLOBファイルへの出力ストリームを生成します。
      os = blob.getBinaryOutputStream();

      //BLOBデータの読み込みに最適なバッファ・サイズを算出し、バッファを生成します。
         byte[] buffer = new byte[ blob.getBufferSize() ];

      //バッファ・サイズ単位でファイルを読み込み、BLOBロケータに書き込みます。
      int length = -1;
      while ( ( length = fis.read( buffer ) ) != -1 ) {
        os.write( buffer , 0 , length );
      }

      return blob.length();

    } catch (Exception e) {
      e.printStackTrace();
      return -1L;
      } finally {
      try { fis.close(); } catch( Exception ignore ) {}
      try { os.close(); } catch( Exception ignore ) {}
    }
  }

}

このJavaクラスでは、BLOBロケータ、ディレクトリ名、ファイル名を引数として受け取り、指定されたファイルからBLOBデータを読み込んでBLOBロケータに流し込んでいます。

次に、コンパイルしたJavaクラスをCREATE JAVA文を使ってOracleにロードします。

CREATE OR REPLACE DIRECTORY JAVA_CLASS_DIR AS 'C:\Temp'
/
CREATE OR REPLACE JAVA CLASS USING BFILE (JAVA_CLASS_DIR, 'UtlLob.class')
/

そして、これをOracleから呼び出せるよう以下のような呼び出しの仕様(コール・スペック)を定義します。

CREATE OR REPLACE FUNCTION UTLLOB_LOADBLOB_FUNC (
  p_blob BLOB, p_dir VARCHAR2, p_file VARCHAR2 
) RETURN NUMBER
AS LANGUAGE JAVA
  NAME 'orcl.UtlLob.loadBlob( oracle.sql.BLOB, java.lang.String,
    java.lang.String ) return long';
/

そしてさらに作成したJavaクラスがファイルにアクセスできるようJAVAのセキュリティを設定します。

CALL dbms_java.grant_permission( 'SCOTT', 'SYS:java.io.FilePermission'
, '<>', 'read' )
/
CALL dbms_java.grant_permission( 'SCOTT', 'SYS:java.io.FilePermission'
, '<>', 'write' )
/
CALL dbms_java.grant_permission( 
  'SCOTT', 'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', ''
)
/
CALL dbms_java.grant_permission( 
  'SCOTT', 'SYS:java.lang.RuntimePermission', 'readFileDescriptor', ''
)
/

以上でようやくファイルにアクセスするJavaストアロプロシージャが準備ができたことになります。

このJavaストアドプロシージャを利用して、BLOBデータをファイルから直接登録するには以下のような手順をとります。

DECLARE
  wk_blob        BLOB;
  wk_length      NUMBER(38);

BEGIN
  --空のBLOBデータを登録
  INSERT INTO TABLEA ( COL1, COL2, COL3 )
    VALUES ( 1, 'TEST.DAT', empty_blob() );
  --BLOBロケータ取得
  SELECT COL3 INTO wk_blob
    FROM TABLEA
    WHERE COL1 = 1
    FOR UPDATE;
  --ファイルをBLOBロケータにロード
  wk_length := UTLLOB_LOADBLOB_FUNC( wk_blob, 'C:\\Temp', 'TEST.DAT' );
END;
/

BLOBロケータを取得するところまではDBMS_LOBパッケージのLOADFROMFILEプロシージャを使う場合と同じ手順で、最後にファイルからBLOBデータをBLOBロケータにロードするところだけが異なります。


その2に続く...



【関連エントリ】
[Oracle] LOBデータの基本的な扱い方 2004/12/03
[Oracle] JDBCによるLOBの操作方法 2004/12/06
[Oracle] LOBデータをファイルから直接登録する方法(その1) 2005/01/25
[Oracle] LOBデータをファイルから直接登録する方法(その2) 2005/01/27
[Oracle] LOBデータをファイルに直接出力する方法 2005/02/01
[Oracle] JavaでLOBデータを読み込む際の最適なバッファサイズは? 2005/02/02
[Oracle] LOB記憶特性 その1(STORAGE IN ROW) 2005/08/23
[Oracle] LOB記憶特性 その2(CHUNK) 2005/08/24