【Salesforce 認定Platformデベロッパー】試験対策:第166問

問題

リードオブジェクトには、カスタム項目の優先メール(Prior_Email__c)があります。以下のトリガーは、メール項目(Email)が変更されるたびに、現在のメール(Email)を優先メール項目(Prior_Email__c)にコピーするものです。

trigger test on Lead (before update) {
    List<Lead> leadsToUpdate = new List<Lead>();
    
    for(Lead leadRecord : trigger.new){
        if(leadRecord.Email != trigger.oldMap.get(leadRecord.Id).Email){
            leadRecord.Prior_Email__c = trigger.oldMap.get(leadRecord.Id).Email;
            leadsToUpdate.add(leadRecord);
        }
    }
    
    if(leadsToUpdate.size() > 0) {
        update leadsToUpdate;
    }
}

このトリガーはどのタイプの組み込み例外を発生させますか。

  • NullPointerException
  • CompileTimeException
  • DmlException
  • LimitException

正解

  • NullPointerException
  • CompileTimeException
  • DmlException
  • LimitException

解説

Salesforceのトリガーは、レコードの変更に応じて自動的に実行されるコードです。トリガーはbeforeafterの2つのタイプに分けられ、それぞれ異なるタイミングで動作します。

  • before update トリガー:
    • 実行タイミング: レコードがデータベースに保存される
    • 特徴: trigger.newを使用して、保存前のレコードの項目値を直接変更できる。
    • 注意点: before update トリガー内でtrigger.newのレコードに対してDML操作(例:update)を行うと、DmlExceptionが発生します。これは、DML操作が新たなトリガーの実行を引き起こす可能性があるためです。また、無限ループのリスクやシステムのパフォーマンスへの影響も考慮されるため、このような操作は許可されていません。
  • after update トリガー:
    • 実行タイミング: レコードがデータベースに保存された
    • 特徴: このタイミングでは、レコードの変更をデータベースに反映させるためにはDML操作が必要。

結論として、before update トリガー内でレコードの変更を行う場合は、フィールドの値を直接変更するだけで良く、DML操作は避けるべきです。逆に、after update トリガーでレコードの変更を行う場合は、DML操作を使用して変更をデータベースに反映させる必要があります。

それぞれの選択肢の理由について説明します。

□ NullPointerException
これは不正解です。このトリガーでは、trigger.oldMap.get(leadRecord.Id).Emailを使用していますが、before updateトリガーのコンテキストでは、trigger.oldMapは更新前のレコードのマップを持っているため、この例外は発生しないと考えられます。

□ CompileTimeException
これは不正解です。Apexの組み込み例外に「CompileTimeException」というものは存在しません。また、このトリガーのコードは正しくコンパイルされるため、コンパイル時の例外は発生しません。

□ DmlException
これは正解です。このトリガーは「before update」のタイミングで動作します。このタイミングでは、データベースに保存される前のレコードの状態を変更することができます。しかし、trigger.newのレコードに対して直接update操作を試みると、Salesforceはそれを許可しません。なぜなら、それは再帰的なトリガーの実行や無限ループを引き起こす可能性があるからです。このトリガーの中で、if(leadsToUpdate.size() > 0) { update leadsToUpdate; }という部分があります。ここでleadsToUpdateリストを更新しようとすると、このリストはtrigger.newの一部として扱われるため、DmlExceptionが発生します。簡単に言えば、before updateの中で既に更新中のレコードを再度更新しようとすると、エラーが発生するのです。

□ LimitException
これは不正解です。このトリガーでは、DML操作はif(leadsToUpdate.size() > 0)の条件の下で行われています。しかし、このトリガーのコード自体は、DML操作やSOQLクエリの制限を超えるような処理を行っていないため、LimitExceptionは発生しないと考えられます。

修正前コード(DmlException発生)

// トリガーの定義: リードオブジェクトが更新される前に実行される
trigger UpdatePriorEmailOnLeadChange on Lead (before update) {
    // 更新するLeadのリストを格納するためのリストを初期化
    List<Lead> leadsToUpdate = new List<Lead>();
    
    // 更新されるすべてのリードレコードに対してループを実行
    for(Lead leadRecord : trigger.new){
        // 現在のEmailと更新前のEmailが異なる場合のみ処理を実行
        if(leadRecord.Email != trigger.oldMap.get(leadRecord.Id).Email){
            // 更新前のEmailをカスタム項目Prior_Email__cにセット
            leadRecord.Prior_Email__c = trigger.oldMap.get(leadRecord.Id).Email;
            // このレコードを更新リストに追加
            leadsToUpdate.add(leadRecord);
        }
    }
    
    // 更新リストにレコードが存在する場合
    if(leadsToUpdate.size() > 0) {
        // ここでDmlExceptionが発生します。before updateトリガー内でtrigger.newのレコードを更新することは許可されていません。
        update leadsToUpdate; 
    }
}

修正後コード

指定された目的に基づいて、DmlExceptionを発生させないようにコードを修正します。

before updateトリガーの中で、trigger.newのレコードを直接変更することで、その変更は自動的に保存されます。そのため、update DML操作は不要です。

trigger UpdatePriorEmailOnLeadChange on Lead (before update) {
    // 更新されるすべてのリードレコードに対してループを実行
    for(Lead leadRecord : trigger.new){
        // 現在のEmailと更新前のEmailが異なる場合のみ処理を実行
        if(leadRecord.Email != trigger.oldMap.get(leadRecord.Id).Email){
            // 更新前のEmailをカスタム項目Prior_Email__cにセット
            leadRecord.Prior_Email__c = trigger.oldMap.get(leadRecord.Id).Email;
        }
    }
    // before updateトリガーの中でtrigger.newのレコードを変更すると、その変更は自動的に保存されるため、DML操作は不要です。
}

この修正により、DmlExceptionは発生しなくなります。

次の問題へ

前の問題へ

1問目から復習する

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

雇われのシステムエンジニアです。
普段は車載ECUのセキュリティー分野に従事しております。

■保有資格
Salesforce 認定 アドミニストレーター
Salesforce 認定 Platform アプリケーションビルダー
Salesforce 認定 Platform デベロッパー

コメント

コメントする

目次