Apexトリガ内で特定の方法でINSERT / UPDATE処理したとき、入力規則エラーが発生するとエラーになったレコードを除外して再実行されるという挙動が確認できます。(以前からあったけ...)
確認方法
次のようにSystem.debugだけを実行するApexトリガを用意しました。
また、"Tom"とセットするとエラーになる入力規則を用意します。
開発者コンソールで登録するデータを用意して『database.insert(contacts, false);』処理でINSERTを実行します。『insert contacts』の場合は一括エラーとなりますが、『database.insert』ならエラーコード以外は正常に登録されます。
デバッグログです。
一度BeforeトリガとAfterトリガを実行した後、エラーレコードを除外して再度実行されていることが確認できます。
もしエラーコードが存在しない場合は、処理は一回のみとなります。
このとき一回目の処理で実行された結果はロールバックされるため、基本的には影響ありません。しかし、同じコンテキスト内の処理として扱われるため、重複処理防止用のフラグなどを用意して判定してい場合は、フラグが変更されたまま再度実行されてしまいます。
重複処理防止用のフラグなどを使用する場合は注意が必要です。
対応方法について
今のところ、重複実行を防止する方法は見当たりませんでしが、今回の入力規則エラー以外にも項目自動更新や他のトリガによる相互更新処理などで複数回実行されるケースが発生すると思われます。
どうしても重複処理防止用のフラグが必要になるケースもあるのですが、重複実行されても問題無いように実装しておくとだいたいは解決すると思います。
例えば次のような仕様の商談用トリガーがあります。
- 商談作成時に活動をINSERT
- 商談の完了予定日を変更時に活動をINSERT
- 商談の説明項目を変更時に取引先をINSERT
- 商談の説明項目は項目自動更新で必ず値がセットされる
(実際にこんな仕様はないと思いますが検証用です...)
「商談の新規作成時にはUPDATEトリガは実行されない。だから商談の変更処理の判定は気にしなくて大丈夫」と実装すると項目自動更新が追加されたときにUPDATEトリガが実行され、本来一件しか作成されないはずの活動が2件作成されるといったことになります。
きちんと特定の値が変更されたか判定しておけば、たとえ項目自動更新が追加されたりしても問題ありません。
また、Apexトリガを開発するときはHandlerクラスやHelperクラスを用意して可読性を良くすることも重要です。1つの処理でループやクエリ実行など全てを詰め込むと次のようにパッと見どこで何をやっているか分からなくなります。
Helperクラスなどに実装することで大分見やすくなります。
処理がスッキリすることで機能追加も簡単になりますし、問題が発生した場合も原因を探しやすくなり対応が楽になります。(やっぱり苦労するなら既存コードを追いかけるところよりも、実装方法とかで苦労したいですね。)
テストクラスとか作っていませんが今回の商談トリガのサンプルです。