見出し画像

【Apex】for文内のSOQL回避について

こんにちは☀️駆け出しエンジニアの小鳥です。
本日はApexのプログラミング中に指摘をうけた標記の件について書いていきます。

Salesforceはマルチテナント環境というもので、ユーザーが共通のインスタンスへアクセスしている?ことから負荷が大きくならないようにガバナ制限というものがあります。上記のリファレンスのようにApexのトランザクション(一連の処理)ごとに何をどれくらいまで出来るみたいなルールが決まってます。

特にSOQLクエリ(レコードの条件指定し検索するみたいな)とDML(データベースへのCRUD)はfor文などの繰り返し処理の中には記載しないようにしないと制限を超えてしまうおそれが。

ということで、安直にコードを書いていたら指摘をいただいたため記録しておこうと思います。要件は削除しようとした商談に関連Todoがあればエラーをだすいうもの。

ハンドラークラス
---NG例---
public void protectOpp(List<Opportunity> deleteOpp) {
        for(Opportunity opp : deleteOpp){
            List<Task> taskList = [SELECT id FROM Task WhatId = opp.id];
       if(taskList == null){
             opp.addError('Msg');
            }
        }
}

レコード削除する操作からApexトリガーが起動し、削除対象の商談レコード(Trigger.ol)を引数に受け取った場合の例です。引数の商談のIDと関連するTodoをクエリしていますが、for文の中なのでNG。

---対策---
//Trigger.oldではなく、Trigger.oldMapで受け取る
public void deleteToDo(Map<Id, Opportunity> oldMap) {
    //keySet()でIdを返し、サブクエリでTaskを取り出す
        List<Opportunity> oppRelTask = [SELECT Id,
                                       (SELECT Subject FROM Tasks LIMIT 1)
                                       FROM Opportunity WHERE Id IN :oldMap.keySet()];
        
            // 商談に関連するTaskでまわす
            for(Opportunity op : oppRelTask){
                // 関連Todoがあったらエラー
                if(op.Tasks.isEmpty() == false) {
                    //oldMapのopのIdがキーの値をゲット、それをandError
                    oldMap.get(op.Id).addError('Msg');
            }
}

for文の外でSOQLをしていて取得した結果をもとにfor文を回しています。
サブクエリとMapの扱いが複雑でわかりにくいですが、これで同じことができます。Mapの使い方がまだ慣れない、、

ご覧いただきありがとうございました👋

いいなと思ったら応援しよう!