トリガー - 典型例
以下は、トリガー・ファンクションの使用方法を示す典型例です。
この例では、複雑な業務上の規則をトリガーに「カプセル化」します。
次に、トリガーを関連データベース・ファイルに関連付け、指定のイベントが発生した場合に必ず業務上の規則で規定された処理が実行されるようにします。
これは、トリガーによって複雑な規則を「カプセル化」し、「オブジェクト」(ファイル)に直接関連付ける方法を明確に示している典型例です。
業務上の問題
ACMEエンジニアリングでは、給与支払いシステムを運用しています。
従業員マスター・ファイル(EMPL)には、"SALARY"および"WEEKPAY"という2つのフィールドが含まれています。
SALARYは、会社が従業員に支払うよう契約している年棒です。
WEEKPAYは、従業員に対して毎週支払われる金額です。
WEEKPAYは、一連の複雑な規則を用いて算出されます。
新しい従業員に対するWEEKPAYの計算は比較的単純ですが、従業員のSALARYが変更された場合は、新しいSALARY額と以前のSALARY額が関与する複雑な計算になります。
トリガー・ファンクション
トリガーを定義するための最初の手順では、すべてのWEEKPAY規則を1つにカプセル化するトリガー・ファンクションを定義します。
これが、優れたトリガー設計の基礎となります。
そのために、以下のファンクションがコーディングされているとします。
FUNCTION OPTIONS(*DIRECT *NOMESSAGES *MLOPTIMIZE)
RCV_LIST(#TRIG_LIST) TRIGGER(*FILE EMPL)
/* Define the standard trigger list which will contain the */
/* before and after images of the EMPL file record. These */
/* fields are automatically added to the list definition */
/* by the RDML compiler. */
DEF_LIST NAME(#TRIG_LIST) TYPE(*WORKING) ENTRYS(2)
/* Now examine exactly what event has occurred */
CASE OF_FIELD(#TRIG_OPER)
/* A new employee is being created */
WHEN VALUE_IS('= BEFINS')
GET_ENTRY NUMBER(1) FROM_LIST(#TRIG_LIST)
<< calculate correct value into field WEEKPAY >>
UPD_ENTRY IN_LIST(#TRIG_LIST)
/* An existing salary has been changed */
WHEN VALUE_IS('= BEFUPD')
DEFINE FIELD(#OLDSALARY) REFFLD(#SALARY)
GET_ENTRY NUMBER(2) FROM_LIST(#TRIG_LIST)
CHANGE FIELD(#OLDSALARY) TO(#SALARY)
GET_ENTRY NUMBER(1) FROM_LIST(#TRIG_LIST)
<< calculate correct value into WEEKPAY >>
<< using OLDSALARY in the calculations >>
UPD_ENTRY IN_LIST(#TRIG_LIST)
OTHERWISE
ABORT MSGTXT('WEEKPAY trigger function invalidly invoked')
ENDCASE
CHANGE FIELD(#TRIG_RETC) TO(OK)
RETURN
トリガー・ファンクションのアクティブ化
これで、トリガー・ファンクションが定義されました。次に、このトリガー・ファンクションをアクティブ化する必要があります。これを行うには、ファイルEMPLの定義にアクセスし、2つのトリガー呼び出しイベントをこの定義に関連付けます。
1つ目は、「INSERT前」として指定し、条件を関連付けません。すなわち、このトリガー・ファンクションは、新しい従業員を作成しようとするたびに呼び出されます。
2つ目は、「UPDATE前」として指定し、以下のような条件を関連付けます。
SALARY NEP SALARY
すなわち、 SALARYが以前のSALARYと等しくない
これにより、トリガーは、従業員のSALARYが変更された場合のみ、「UPDATE前」にアクティブ化されます。
「UPDATE前」イベントをこのように定義すると、従業員のSALARYが変更されない(ほとんどがこの状況に該当)限りトリガーはアクティブ化されないため、非常に効率的です。
SALARYが変更されるか、従業員が勤務するCOMPANYが変更されたときに、WEEKPAYを再計算する必要がある場合は、代わりに以下のような呼び出しイベントを定義します。
SALARY NEP SALARY
OR COMPANY NEP COMPANY
すなわち、 SALARYが以前のSALARYと等しくない
または COMPANYが以前のCOMPANYと等しくない
WEEKPAYを必ず再計算する場合は、2つの呼び出しイベントを別々に定義する必要はありません。単純に1つのイベント(条件なし)を定義し、「INSERT前」および「UPDATE前」にトリガーを呼び出すよう指定します。
この場合、当然ながら、従業員の挿入または更新を行うたびに、トリガー・ファンクションが呼び出されることになります。
この例についての注意点
この例には、優れたトリガーの設計と使用のために重要になる要素がいくつか示されています。
· 「カプセル化」原理。WEEKPAYを計算するための「具体的な処理」が、1つのファンクションに「カプセル化」されています。そのため、必要な変更を1箇所で行えます。
· 必要が生じてからの設計。システム設計の初期段階では、WEEKPAYの具体的な処理の存在を定義する必要はありません。また、具体的な処理を想定して全体の設計を進める必要もありません。
逆に言うと、設計工程のどの時点でも、必要になった時点で具体的な中身を決めてよい、ということです。例えば、従業員を作成したり更新したりするアプリケーションが決まっていない段階で、WEEKPAYの具体的な処理を定義する必要はありません。まず、作成/更新アプリケーションを定義し、検査することができます。WEEKPAYの具体的な処理を作成、定義した時点から、既存のアプリケーションの処理に反映されるようになります。
· 再利用可能性。WEEKPAYを計算する具体的な処理は、従業員の詳細を作成または変更するアプリケーションによって自動的かつ暗黙的に再利用されます。トリガーは、通常のNPTデバイスから「従業員データ保守」ファンクションを通して、またはPCアプリケーションからLANSA Open機能を通してアクティブ化される可能性があります。
· 情報隠蔽。WEEKPAYというロジックが存在し、使用されているという事実は、「従業員データ保守」ファンクションを作成するRDMLビルダーからは見えません。また、RDMLビルダーにとっては意味がないものでしょう。
· 「具体的な処理」と「イベント」の分離。トリガー・ファンクションでは、「イベント」が発生したときに何を行うか(すなわち「具体的な処理」)を定義します。
ただし、イベントの発生を検出する必要はありません。
例えば、前に定義したファンクションでは、「週給の計算」という「具体的な処理」を定義しています。
業務上の規則では、新しい従業員が採用された場合、既存の従業員の給与が変更された場合、または既存の従業員が転職した場合に、週給を(再)計算しなければならないことを規定しています。
実際の「イベント」は、LANSAデータ・ディクショナリで定義します。
· Ýトリガー・ファンクション