みなさん、こんにちは。 Apexトリガ書いてますか? Apexトリガレビューしてますか?? 我々が導入を支援させていただいているお客様のForce.comで構築されているシステムでも、多くのApexトリガが実装されています。 また、自分自身ココ1年くらいの期間で思い起こすと、ほぼすべてのプロジェクトで何かしらのApexトリガを実装していることに気が付きました。 Apexトリガが身近な存在(?)になっているということですね。今日は、Apexトリガをコードレビューする際のポイントをまとめてみました。
Apexトリガのレビューポイントは、次の3つです。
- 複数レコード更新を考慮した実装となっているか?
- ガバナ制限を理解した実装となっているか?
- Apexトリガの実行順序を正しく考慮した実装となっているか?
※もちろんビジネスロジックの妥当性のレビューも必要です。
1.複数レコード更新を考慮
Apexトリガコンテキスト内ではTrigger.NewやTrigger.Old変数にレコードのリストが格納されています。このリストには最大200件のリストが格納されている可能性があります。 リリース時点で、「Bulk APIやSOAP API、WebServiceを利用した更新がない」や「ページから1レコードずつの保存しか行わない」という場合でも、上記リストでの更新を考慮した実装が行われているか確認しましょう。 例えば、「画面から更新した場合には期待通りにApexトリガが動作するのに、DataLoaderで登録を行ったら最初の1件しかApexトリガで更新されていなかった。」というのは良くある失敗例ですし、ビューの一括更新機能を利用できる場合、ユーザは画面からも複数レコードの一括更新を行うことができます。
Trigger.New[0]などトリガ変数にインデックス固定でアクセスしているときは要注意
2.ガバナ制限
ガバナ制限は、Force.comでの開発を行う皆さんにとってはもう馴染みの特徴ですね。 このガバナ制限とうまく付き合うことにより効率的なシステムの構築が可能になります。 ※ガバナ制限についてはコチラ Apexトリガ実装時に抵触しがちなガバナ制限ごとに、レビューポイントを見てみましょう。
SOQL、DMLの発行回数
1コンテキスト内で発行可能なSOQLやDMLには上限があります。
for文内で、SOQLやDMLを発行していないか確認します。
下記の例の場合、6行目のSOQLは最大200回呼出しされるため、ガバナ制限に抵触します。
スクリプトステートメント
for文をネストさせて、Key値によるレコードのマッチングを行っていないか確認しましょう。 ※ChildRelationShipQueryを利用している箇所以外では極力for文のネストを行わないで実装しましょう。
for文をネストさせているところを確認します。
selectiveなSOQL
初回リリース時に見落としがちな制限事項です。 Apexトリガ内では10万件以上レコードが存在するオブジェクトに対して、SOQLを投げる場合に、WHERE句に有効なインデックス項目を含める必要があります。 リリース時には10万件に満たない場合でも、将来的に10万件を超える可能性があるオブジェクトに関しては、必ずWHERE句に有効なインデックス項目が含まれていることを確認しましょう。 ※有効なインデックスについてはSOQLクエリのパフォーマンスチューニングも参照しておいてください。
3.実行順序
ここで特に問題となりやすいのは、ワークフロールールなどで項目自動更新が実装されている場合、画面などからの通常の更新の後に、再度項目自動更新によるApexトリガが発火することです。 例えば、オブジェクトAのレコード保存時に発火するApexトリガで、オブジェクトBにレコードをInsertしている処理があるとします。 オブジェクトAには項目自動更新が設定されているときに、オブジェクトBには2回Insertされてしまう可能性があります。 また、2回トリガが発火することにより、スクリプトステートメントを約2倍消費する可能性があります。 トリガの発火状態を保持するstaticな変数を利用して、2重起動を抑止させましょう。コチラ