問題
リードオブジェクトには、カスタム項目の優先メール(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のトリガーは、レコードの変更に応じて自動的に実行されるコードです。トリガーはbefore
とafter
の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
は発生しなくなります。
コメント