【Salesforce】Trigger処理時の参照項目で少しハマった
レコードが更新・削除された際に、関連するレコードの操作を行いたいということはよくありますね。
そんなとき、Apexトリガを使用して操作を行うのが一般的だと思います。
しかし、処理をするタイミングと内容で参照項目の中身が変化することに注意が必要です。
https://trailhead.salesforce.com/ja/modules/apex_triggers/units/apex_triggers_intro
例えば、こんな感じの取引先トリガがあります。
trigger AccountTrigger on Account (before insert, before update, before delete, after insert, after update, after delete) { AccountTriggerHandler handler; // ハンドラクラスを作成する handler = new AccountTriggerHandler(); // Before Insert if(trigger.isBefore && trigger.isInsert){ handler.beforeInsert(trigger.new); } // Before Update if(trigger.isBefore && trigger.isUpdate){ handler.beforeUpdate(trigger.new, trigger.old); } // Before Delete if(trigger.isBefore && trigger.isDelete){ handler.beforeDelete(trigger.old); } // After Insert if(trigger.isAfter && trigger.isInsert){ handler.afterInsert(trigger.new); } // After Update if(trigger.isAfter && trigger.isUpdate){ handler.afterUpdate(trigger.new, trigger.old); } // After Delete if(trigger.isAfter && trigger.isDelete){ handler.afterDelete(trigger.old); } }
単純にトリガハンドラを呼び出すだけのトリガですね。
こちらはトリガハンドラクラスです。
public class AccountTriggerHandler { private List<Account> newList; private List<Account> oldList; private Map<Id, Account> newMap; private Map<Id, Account> oldMap; /** * 値の初期化を行う */ public AccountTriggerHandler (){ this.newList = new List<Account>(); this.oldList = new List<Account>(); this.newMap = new Map<Id, Account>(); this.oldMap = new Map<Id, Account>(); } /** * Before Insert 処理 * @param newAccountList Insert対象の取引先リスト */ public void beforeInsert(List<Account> newAccountList){ this.newList = newAccountList; } /** * Before Update 処理 * @param newAccountList Update対象の取引先リスト * @param oldAccountList Update対象の取引先リスト */ public void beforeUpdate(List<Account> newAccountList, List<Account> oldAccountList){ this.newList = newAccountList; this.oldList = oldAccountList; this.newMap = new Map<Id, Account>(this.newList); this.oldMap = new Map<Id, Account>(this.oldList); } /** * Before Delete 処理 * @param oldAccountList Delete対象の取引先リスト */ public void beforeDelete(List<Account> oldAccountList){ this.oldList = oldAccountList; this.oldMap = new Map<Id, Account>(this.oldList); } /** * After Insert 処理 * @param newAccountList Insert対象の取引先リスト */ public void afterInsert(List<Account> newAccountList){ this.newList = newAccountList; this.newMap = new Map<Id, Account>(this.newList); } /** * After Update 処理 * @param newAccountList Update対象の取引先リスト * @param oldAccountList Update対象の取引先リスト */ public void afterUpdate(List<Account> newAccountList, List<Account> oldAccountList){ this.newList = newAccountList; this.oldList = oldAccountList; this.newMap = new Map<Id, Account>(this.newList); this.oldMap = new Map<Id, Account>(this.oldList); } /** * After Delete 処理 * @param oldAccountList Delete対象の取引先リスト */ public void afterDelete(List<Account> oldAccountList){ this.oldList = oldAccountList; this.oldMap = new Map<Id, Account>(this.oldList); } }
インスタンス変数のリストへ引数を移送し、そのリストに対応するMapを作成しています。
しかし、beforeInsert処理は他の処理と異なり、newMapを作成していません。
BeforeInsertのタイミングではレコードにIdが設定されていないため、Mapを作成するとエラーになってしまうからですね。
上のURLにある通り、まだデータベースへレコードが保存されていない状態だからですね。
今度は、afterDeleteの処理にこのような記述をします。
/** * After Delete 処理 * @param oldAccountList Delete対象の取引先リスト */ public void afterDelete(List<Account> oldAccountList){ Set<Id> accountIdSet; List<Account> childAccountList; this.oldList = oldAccountList; this.oldMap = new Map<Id, Account>(this.oldList); // 取引先IdSetを作成する accountIdSet = new Set<Id>(); for(Account acc : this.oldList){ accountIdSet.add(acc.Id); } // 子会社の取引先を取得する childAccountList = [select Id from Account where ParentId IN :accountIdSet]; // 子会社の取引先リストを更新する if(childAccountList.size() > 0){ update childAccountList; } }
親会社が削除された際に、紐づく子会社に対して更新処理を行っています。
しかし、このupdate処理は動くことがありません。
AfterDeleteのタイミングでは子会社側の参照項目は既にnullとなっているためです。
この処理を行う場合、BeforeDeleteのタイミングで処理を行う必要があります。
/** * Before Delete 処理 * @param oldAccountList Delete対象の取引先リスト */ public void beforeDelete(List<Account> oldAccountList){ Set<Id> accountIdSet; List<Account> childAccountList; this.oldList = oldAccountList; this.oldMap = new Map<Id, Account>(this.oldList); // 取引先IdSetを作成する accountIdSet = new Set<Id>(); for(Account acc : this.oldList){ accountIdSet.add(acc.Id); } // 子会社の取引先を取得する childAccountList = [select Id from Account where ParentId IN :accountIdSet]; // 子会社の取引先リストを更新する if(childAccountList.size() > 0){ update childAccountList; } }
恥ずかしながら、このAfterDelete時の処理で子レコードが更新されないと1時間くらいハマってしまいました。
トリガ内での挙動については認識していたつもりでしたが、まだまだですね。
No comments.