4-2. ファイル入出力の共通化

コーディング量の削減

最初に考えられるのはファイルの入出力です。
前章で述べた「コーディング量の削減」という目的から、まず、呼び出し側のパラメータを最小化することを考えます。

ファイルからの入力を行うのに必要な情報は、
・ファイルの出力先ディレクトリ
・ファイル名

ファイルへの出力を行うために必要な情報は、
・ファイルの出力先ディレクトリ
・ファイル名
・出力データ

となります。

実装に際しては、UTL_FILEパッケージを使用することになりますが、UTL_FILEパッケージを使用する際に必要なパラメータは何でしょうか。

ファイルの入出力には、ファイルオープンという処理が必要です。
ファイルオープン時に、ファイル入出力先を示すファイルハンドルを取得し、ファイルへの書込み・ファイル出力は、ファイルハンドルに対して行います。
しかし、前述のようにファイルの入出力を指示する側にとっては、このファイルハンドルの存在は直接必要なものではありません。
ディレクトリとファイル名さえあれば、ファイル出力することは可能です。

ファイルオープンに際しては、ファイルの入出力モードの指定、行サイズの指定が必要ですが、ファイルの出力関数が「入力」であれば、関数内部で自動的に読み取りモードでオープンし、「出力」であれば書込みモードでオープンすれば良いのです。
書込みモードに際して、W(書込)とA(追記)のモードが分かれていますが、大抵の場合は追記モードで良いはずです。

同一ファイル名に対して複数セッションからの書込みの可能性がある場合は、まずファイル名が一意になるような設計を検討すべきです。

つまりは、開発者にはファイルのオープン・クローズを意識させることなく、共通関数内でオープン・クローズを行うことで、パラメータを最小化することができるのです。

ただし、ファイルのオープン・クローズは、一昔前であれば非常に負荷の大きな処理でした。
性能要件の厳しいシステム、ディスクI/O性能がネックになるようなシステムでは、従来通り開発者がファイルのオープン・クローズを制御した方が良い場合もありますので、十分に検討してください。

また、ファイルオープン・クローズの制御を開発者に任せると、例外処理の考慮も必要です。
カーソル制御もそうですが、一度オープンしたファイルはプログラムの終了時に必ずクローズする必要があります。
バグでファイルクローズの制御を通らない可能性が出てきてしまいます。
共通関数化することで、これも回避することができます(もちろん、共通関数内で確実にファイルクローズするようロジックを記述する必要があります)。

最小限の例として、下記のようなコードになるのではないでしょうか。
例外処理がシンプルに記述していますが、システムに応じて例外処理を追記してください。


CREATE PROCEDURE xxx_write(
lv_file_dir IN VARCHAR2, -- ディレクトリオブジェクト名
lv_file_name IN VARCHAR2, -- ファイル名
lv_message_text IN VARCHAR2) -- 出力文字列
IS
file_type utl_file.file_type;
BEGIN
-- ファイルオープン
-- lv_file_dirは出力先のディレクトリオブジェクト。
file_type := utl_file.fopen(lv_file_dir, lv_file_name, 'a', 32767);
-- ファイル書込み
utl_file.put_line(file_type, lv_message_text);
-- ファイルクローズ
utl_file.fclose(file_type);
EXCEPTION
WHEN OTHERS THEN
IF utl_file.isopen(file_type) THEN
utl_file.fclose(file_type);
END IF;
RAISE;
END xxx_write;

 

コーディングの標準化

各機能がUTL_FILEパッケージを直接呼び出すようにしてしまうと、将来的にOracleのバージョンアップ等によるUTL_FILEの仕様変更があった際に、変更の内容次第では全プログラムの改修が必要になる可能性があります。

また、UTL_FILEパッケージは様々な機能を提供していますので、開発者に自由に使わせると、コーディングの統一感がなくなってしまいます。

上記のように、パラメータを最小化し、ファイルのオープン・クローズまで共通関数に内包することで、ファイル制御に関するコーディングも自ずと統一されていくことになります。

Posted by tfurukaw