トリガのエラーケースいろいろ

今回はトリガのエラーについて、上級試験となると使い方は流石に分かっている人が多いんですが、今回はあえてエラーケースについて考察してみます。

スポンサーリンク

早速だけど検証から

今回は同じレコードを更新させるトリガを作成します。
取引先を対象として、更新時に従業員数を+1する処理を入れます。
それぞれ、before・afterトリガでやってみたらどうなるでしょうか?

Beforeトリガの中で更新してみる①

まずはBeforeトリガの検証から。

早速、以下ソースコードを作って取引先を更新させてみました。
検証のためのコードで、ちゃんとしたコードではないので悪しからず。
良い子はマネしちゃダメなコードだよ!

CountUpNumberOfEmployees.trigger

trigger CountUpNumberOfEmployees on Account (before update) {
    Account a = (Account)Trigger.new[0];
    a.NumberOfEmployees = a.NumberOfEmployees + 1;
    update a;
}

取引先を更新すると、このようなエラーメッセージが出ました。失敗です。

エラー: 無効なデータです。
以下のエラーメッセージを参照して修正してください。
Apex トリガであるCountUpNumberOfEmployeesで予期せぬ例外が発生しました。システム管理者にお問い合わせください。CountUpNumberOfEmployees: execution of BeforeUpdate caused by: System.SObjectException: DML statement cannot operate on trigger.new or trigger.old: Trigger.CountUpNumberOfEmployees: line 7, column 1

メッセージにもありますように、Trigger.oldまたはTrigger.newで取得したレコード自体ではUpdateなどの明示的なDML操作ができないというエラーです。
Beforeトリガなので、しれっと値を書き換えるのが常識ですね。

Beforeトリガの中で更新してみる②

次はTriggerクラス内のレコードがダメなので、DBから取得したレコードでUpdate掛けてみました。
CountUpNumberOfEmployees.trigger

trigger CountUpNumberOfEmployees on Account (after update) {
    Account a = (Account)Trigger.new[0];
    a = [Select Id, NumberOfEmployees From Account Where Id = :a.Id];
    a.NumberOfEmployees = a.NumberOfEmployees + 1;
    update a;
}

実行結果はこう。

エラー: 無効なデータです。
以下のエラーメッセージを参照して修正してください。
Apex トリガであるCountUpNumberOfEmployeesで予期せぬ例外が発生しました。システム管理者にお問い合わせください。CountUpNumberOfEmployees: execution of BeforeUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 0011000001LePKyAAN; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 0011000001LePKy) is currently in trigger CountUpNumberOfEmployees, therefore it cannot recursively update itself: []: Trigger.CountUpNumberOfEmployees: line 7, column 1

トリガによって動いているプロセス中で、自身のレコードをUpdateしようとしているため、NGを出しています。「今処理中だから待って」という感じのエラーです。

Afterトリガの中で更新してみる①

CountUpNumberOfEmployees.trigger

trigger CountUpNumberOfEmployees on Account (after update) {
    Account a = (Account)Trigger.new[0];
    a.NumberOfEmployees = a.NumberOfEmployees + 1;
    update a;
}

取引先を更新すると、このようなエラーが出ます。

エラー: 無効なデータです。
以下のエラーメッセージを参照して修正してください。
Apex トリガであるCountUpNumberOfEmployeesで予期せぬ例外が発生しました。システム管理者にお問い合わせください。CountUpNumberOfEmployees: execution of AfterUpdate caused by: System.FinalException: Record is read-only: Trigger.CountUpNumberOfEmployees: line 3, column 1

まぁ、メッセージから大体推測できますが、Afterトリガでは、Trigger.Newから取得したレコードは編集不可なので、このような「Record is read-only」というメッセージが出るわけです。

Afterトリガの中で更新してみる②

次は1行だけ追加しました。
Triggerクラスから取得されたオブジェクトの直接編集がNGなので、一度DBから拾って更新するとどうなるか?
CountUpNumberOfEmployees.trigger

trigger CountUpNumberOfEmployees on Account (after update) {
    Account a = (Account)Trigger.new[0];
    a = [Select Id, NumberOfEmployees From Account Where Id = :a.Id];
    a.NumberOfEmployees = a.NumberOfEmployees + 1;
    update a;
}

結果はこうなりました。

エラー: 無効なデータです。
以下のエラーメッセージを参照して修正してください。
Apex トリガであるCountUpNumberOfEmployeesで予期せぬ例外が発生しました。システム管理者にお問い合わせください。CountUpNumberOfEmployees: execution of AfterUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 0011000001LePKyAAN; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, CountUpNumberOfEmployees: maximum trigger depth exceeded Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate Account trigger event AfterUpdate: []: Trigger.CountUpNumberOfEmployees: line 5, column 1

とても長ったらしい文章なんですが、よく見ると途中同じような文言の繰り返しになっていますね。

これは、以下のガバナ制限

insert、update、または delete ステートメントによって繰り返しトリガする Apex 呼び出しのスタックの深さの合計数:16

を超過したことに対すエラーで、スタック深さの上限を超えているんですね。
エラーが起ったトリガからキッカケとなったトリガをトレースして、数珠つなぎ的にメッセージを出力してるんですね。
今回はCountUpNumberOfEmployeesトリガからCountUpNumberOfEmployeesトリガを起こさせるようにしているので、途中「Account trigger event AfterUpdate」が繰り返されているわけです。

エラーの見極めが重要

このように、ソースをちょっと変えただけなのに、出るエラー内容がそれぞれ異なりましたね。
beforeトリガ内の制約、afterトリガ内の制約、そしてガバナ制限とどの制約に引っ掛かっているかを見極める必要があります。

上級の試験となると、こういうわざとエラーを起こして、どういったエラーかと問われる問題もあるので、エラーの種類も覚えておきましょう。

まとめ

  • Triggerクラスから取得したレコードはDML操作(明示的なupdateなど)ができない
  • Triggerクラスから呼び出せる最大の階層数は16

コメント