プロシージャコール

プロシージャコールとは?

DBFluteでは、ストアドプロシージャ、および、ストアドファンクションなどのDBに組み込まれた処理のことをひっくるめて プロシージャ と呼びます。そして、アプリなどからのプロシージャ呼び出しをプロシージャコールと呼びます。 また、DBFluteのプロシージャコール機能を ProcedureCall と表現します。

プロシージャコールの悩み

プロシージャ内部の実装については、DBFluteでは何も関与しません(できません)。 ここでは、アプリからのプロシージャコールの際の実装上の悩みに着目します。以下のような悩みが考えられます。

プロシージャの名前
呼び出すプロシージャの名前の指定でスペルミスでの呼び出しミス
パラメータ
プロシージャのパラメータ(引数)の名前の指定でスペルミスでの設定ミス
結果セット(ResultSet)
プロシージャの結果セットの受け取り用 Entity を作成(スペルミスでのマッピングミス)
プロシージャ変更
結果、全てタイプセーフではないため、プロシージャの変更(DB変更とも言える)に弱い

およそ、DBFluteを使わずにSQLをJDBCダイレクトに発行させたときに発生する悩みとほぼ同じです。 結局、何も対策していなければ、"呼び出し" というレイヤにおける悩みに変わりはありません。特に "プロシージャ変更" に関しては、アプリのプログラムとプロシージャのプログラムは遠い関係にありがちで(作成者も違えば、管理組織も違うかも)、 同期漏れのデバッグコストが発生する可能性を秘めています。

タイプセーフなプロシージャコール

対応クラスの自動生成

DBFluteでは、プロシージャコールはタイプセーフ、そして、プロシージャ変更にも強い をポリシーとしています。具体的には、以下の通りです。

  • プロシージャのメタデータからプロシージャ対応のクラス(ParameterBean)を自動生成
  • プロシージャ実行のメタデータから結果セット対応のクラス(CustomizeEntity)を自動生成

やっていることは、実は通常の外だしSQL(OutsideSql)と同じことです。どちらかというと、プロシージャも "外に出しているSQL" というニュアンスで、広い意味で外だしSQLと捉えることができます。実際に DBFlute のプロシージャコールの実装方法は、ほぼ外だしSQLと同じやり方で呼び出します。

プロシージャ変更に強い

これにより、プロシージャコールの悩みは吹き飛びます。

プロシージャの名前
プロシージャの名前に対応した ParameterBean を利用することで呼び出しミスは発生しない
パラメータ
パラメータ(の名前)に対応したプロパティを利用することで設定ミスは発生しない
結果セット
結果セット(select句)に対応した Entity を利用することでマッピングミスは発生しない
プロシージャ変更
プロシージャの名前変更、パラメータ変更(パラメータの増減、パラメータ名や型の変更)、結果セットの変更(select句の構成変更)は、 プログラムのコンパイルエラーとなって検知でき、同期漏れは発生しない。

利用方法の概要

プロシージャ対応の ParameterBean や CustomizeEntity は、外だしSQLのと同じく Sql2Entity タスクにて自動生成できます。プロシージャ対応の ParameterBean を ProcedurePmb と呼びます。 ProcedurePmb の仕様がそのまま、DBFluteにおけるプロシージャへのアプローチ(サポート状況も含めて)を表現していると言えます。

クラスができ上がったら、あとはそのクラスを使ってアプリから呼び出すだけです。

IN, OUTパラメータ

対応するプロパティ経由で、INパラメータは call() 前に設定し、OUTパラメータは call() 後に取得します。 また、INOUTパラメータは、INパラメータとOUTパラメータの両方の特性を持ちます。

e.g. IN, OUT, INOUT パラメータ (SP_IN_OUT_PARAMETER) {MySQL} @Procedure
create procedure SP_IN_OUT_PARAMETER(
      in v_in_varchar varchar(32)
    , out v_out_varchar varchar(32)
    , inout v_inout_varchar varchar(32)
)
begin
  set v_out_varchar = 'qux';
  set v_inout_varchar = 'quux';
end;
e.g. IN, OUT, INOUT パラメータの利用 (SP_IN_OUT_PARAMETER) @Java
SpInOutParameterPmb pmb = new SpInOutParameterPmb();
pmb.setVInVarchar("foo"); // INパラメータの設定
//pmb.setVOutVarchar("bar"); // OUTパラメータなので設定なし
pmb.setVInOutVarchar("baz"); // INOUTパラメータの設定
xxxBhv.outsideSql().call(pmb);
String outParam = pmb.getVOutVarchar(); // OUTパラメータの受け取り (qux)
String inOutParam = pmb.getVInOutVarchar(); // INOUTパラメータの受け取り (quux)

プロシージャリターン

プロシージャリターン値(戻り値)も受け取れます。OUTパラメータと同じ扱いとなります。

e.g. プロシージャリターンのあるプロシージャ (SP_RETURN_PARAMETER) {PostgreSQL} @Procedure
create or replace function SP_RETURN_PARAMETER()
returns integer as
$BODY$
begin
  return 1;
end;
$BODY$ LANGUAGE 'plpgsql';
e.g. プロシージャリターンの利用 (SP_RETURN_PARAMETER) @Java
SpReturnParameterPmb pmb = new SpReturnParameterPmb();
xxxBhv.outsideSql().call(pmb); // 実行
Integer returnValue = pmb.getReturnValue(); // プロシージャリターンの受け取り (1)

結果セット (ResultSet)

結果セット(ResultSet)は、コール後に対応するプロパティから Entity (のリスト)を取得します。

e.g. OUTパラメータの結果セットの受け取り {PostgreSQL} @Java
create or replace function SP_FOO_PROC(cur_member out refcursor)
as
$BODY$
begin
  open cur_member for
    select MEMBER_ID, MEMBER_NAME, BIRTHDATE
      from MEMBER;
end;
$BODY$ LANGUAGE 'plpgsql';
e.g. OUTパラメータの結果セットの受け取り {PostgreSQL} @Java
SpFooProcPmb pmb = new SpFooProcPmb();
memberBhv.outsideSql().call(pmb);
List<SpFooProcCurMember> memberList = pmb.getCurMember();
for (SpFooProcCurMember member : memberList) {
    ... = member.getMemberId();
    ... = member.getMemberName();
    ... = member.getBirthdate();
}

プロシージャ呼び出しフレームワーク

DB変更に強いをポリシーとする DBFlute は、そのDB変更という言葉にプロシージャ実装の変更も含みます。基本的には ConditionBean や外だしSQLなどを使ってDBアクセスをすることが前提のポリシー ですが、いざというときのパフォーマンスを発揮するプロシージャ、そして、 組織文化によってプロシージャでのDBアクセスをメインすることもあるため、様々な場面でプロシージャが活躍することがあります。

DBMSごとの方言も(かなり)激しく、Java や C# などとは全く違った世界が展開されているため全ての機能をサポートするのは難しいですが、プロシージャを目の前にした途端に DBFlute の良さが完全には消えてしまう、ということがないように プロシージャ呼び出しフレームワーク としても有用であるようにと作られています。

プロシージャ一覧の生成

プロシージャの一覧を SchemaHTML で参照することができます。どんなプロシージャがあるのか、ディベロッパーがすぐに把握できます。 アーキテクトもプロシージャを管理する上で役に立つでしょう。

DBMSごとのプロシージャの取扱い

プロシージャは DBMS ごとの方言がとても激しいものです。複雑な構造のものはサポートされない場合もあります。 DBMSごとの取扱いを参考に、利用する DBMS でのプロシージャの取扱いの特徴をよく押さえて利用すると良いでしょう。

フィードバックの重要性

プロシージャの世界は広く、(DBFluteとして)未到達の部分も多いです。いざ呼び出せないプロシージャなどに直面したときには フィードバック を頂ければ、実現可能性次第で対応される可能性もあるでしょう。 特にプロシージャに関しては、トップダウンで全ての機能・事象を把握することが困難なため、 要望ベースのボトムアップで対応していくというスタンスをとっています。