Developer

トリガを作成する際の注意点【セールスフォース】

Developer
この記事は約4分で読めます。

こんにちは、アンダーソンです。
今回はApexトリガについて解説していこうと思います。
自身の経験や公式ドキュメントの情報などをお伝えしていきます。


きっかけは7種類

トリガは引き金と呼ばれているように、ある引き金が引かれることで動くプログラムです。
セールスフォースでは、基本的にトリガはオブジェクトに依存し、そのオブジェクトのレコードにDML操作が
実行されることで発火します。

DML操作の種類は4種類あります。
・insert
・update
・delete
・undelete

さらにundelete以外の3種類には、データベースに保存される前【Before】か後【After】でも
判定を持っています。

記述は下記のように行います。

trigger trgBeforeApplicant on Applicant__c (before insert, before update, before delete) {
   //処理を書く
}

このように()内へ発火するタイミングの引数を渡しておく必要がありますので注意しましょう。
※上記の場合、After insertは使えない。

コンテキストを覚えておく

トリガのコンテキストは基本的にほぼ覚えてしまいましょう。
特に複数のDMLによるトリガを持つ場合はその条件によって行う処理も変わってくるかと思います。
そう言った際には

if ( Trigger.isInsert ) {
   //処理を書く
}

このようにしておけばインサート時に狙った処理をうごかすことができます。
また前なのかの判定はisBefore、後なのかはisAfterで判別することができます。
参考

トリガを作る際に気をつけること

トリガは基本的に大量なデータを扱い、データの品質の向上にも貢献してくれます。
1件づつレコードが入ってきて、一件づつを処理していくなら、いつものApexクラスの感覚で問題ないと思います。
ですが、インポートウィザードやデータローダなどで大量のレコードがトリガ対象になるかもしれません。
そんな際に気をつけたいのがガバナ制限です。

トリガ内では基本的に、
・ループ内でSOQL文(クエリの発行をしない。)
・ループ内でDML処理をしない。
・DML操作は単一レコードで行わない。

としておくのが賢明だと思います。
ループ内でSOQLやDML操作をすると件数によってはあっという間にガバナ制限に抵触する
可能性がありますので、極力控えましょう。

コードをよりよくするために

前述のガバナ制限に加えて、できるだけ時間をかけない実装を意識することも大事です。
例えば10000件のListがあったとします。
取得したいのは9999件目だった場合にListでは0から(インデックスの始めから)ループが始まります。
そうではなくてMapのkeyset()メソッド、value()メソッドを使い、ループにかかる時間をできるだけ抑えます。

またSOQLでレコードを探す際にもIN句ないでSetを使ってできる限りスコープを狭くして、
CPUTimeを短くする工夫をします。

複数ループに気を付ける

初めてトリガを作った際に、Aオブジェクトを更新すると連動してBを更新する。
またBが更新されると連動した値がAに反映される。
という案件があり、見事に無限ループにハマった記憶があります。

ガバナ制限では、

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

Apex ガバナ制限

となっています。
これを回避するためにはトリガから呼び出すハンドラクラスを実装し、静的な変数を持たせて対処します。

static boolean isFirstTime = false;

って具合に宣言しておき、一回目の更新が終わるタイミングでTrueに変換。
次の更新時には変数がTrueかどうかをみてTrueであれば処理しないという実装にします。
こうすることで複数ループを回避することもできますので、ぜひやってみてください。

BeforeなのかAfterなのか

DML操作によっては使えるコンテキストが変化するものがあります。
それはレコードのコレクションです。
例えばBefore Insertの場合、使えるコンテキストはTrigger.Newのみです。
しかし、After InsertであればTrigger.NewとTrigger.NewMapを使うことができます。

これはレコードが入ってくるタイミングによってセットされているものが違うため起こります。
上記の場合であれば、Insert前にはまだレコードが保存されていない=SFIDがないのでMapが使えません。
※Trigger.NewMapはMap<Id, sObject>の形です。

ただし、Beforeは入ってきた値をDML操作なしで上書きすることもできます。
※Deleteは例外。

どちらのコンテキストがいいかは組織の状況や、やるべきことに合わせていけばいいのかなと思います。

その他の開発に関する記事はこちらです。

Developerも含めた試験問題にチャレンジしてみましょう。

コメント