Part 8: トリガー (Trigger) のエラーハンドリングとリカバリ方法
前回は エラーログ管理 と リトライ機構 を解説しました。
今回は Apex トリガーにおけるエラーハンドリング と、エラー発生時のリカバリ方法 について解説します。
1. トリガーのエラーハンドリングの基本原則
1.1. トリガーでのエラーの特徴
Apex トリガーでは、エラーが発生するとその処理全体がロールバック されます。
つまり、エラーが発生したレコードだけでなく、処理中のすべてのレコードが保存されません。
例えば、before insert トリガーで 10 件のレコードを処理中に 1 件がエラーになった場合、
すべてのレコードがロールバック されます。
1.2. トリガーのエラーハンドリング戦略
トリガー内でのエラーハンドリングの考え方は以下の 3 つです。
| 戦略 | 概要 | 適用ケース |
|---|---|---|
| 全体をロールバック | 例外をスローして処理を中断 | データ整合性を最優先する場合 |
| 部分的にエラーを記録 | エラーのあるレコードのみ処理対象から除外 | エラーを許容できる場合 |
| エラーレポートを作成 |
addError() を使用して、エラーをレコード単位で通知 |
ユーザーにエラーをフィードバックしたい場合 |
2. 例外をスローして処理を中断する (全体をロールバック)
✅ NG 例: トリガー内で直接 try-catch
trigger AccountTrigger on Account (before insert) {
try {
for (Account acc : Trigger.new) {
acc.Name = acc.Name.toUpperCase(); // 例: アカウント名を大文字にする
}
} catch (Exception e) {
System.debug('エラー発生: ' + e.getMessage());
// → ただのデバッグログなので、処理は継続してしまう
}
}
このコードでは エラーがキャッチされても処理が継続するため、問題のあるデータがそのまま保存されるリスクがあります。
✅ OK 例: TriggerHandler クラスでエラーを適切に処理
トリガー内ではなく 別のクラスでエラーハンドリングを行い、適切にスローする のがベストプラクティスです。
🔹 AccountTriggerHandler クラス
public class AccountTriggerHandler {
public static void beforeInsert(List<Account> newAccounts) {
for (Account acc : newAccounts) {
if (String.isEmpty(acc.Name)) {
throw new CustomTriggerException('アカウント名が空です');
}
acc.Name = acc.Name.toUpperCase();
}
}
}
🔹 CustomTriggerException クラス
public class CustomTriggerException extends Exception {}
🔹 AccountTrigger (トリガー)
trigger AccountTrigger on Account (before insert) {
try {
AccountTriggerHandler.beforeInsert(Trigger.new);
} catch (CustomTriggerException e) {
System.debug('エラー発生: ' + e.getMessage());
throw e; // 例外をスローしてロールバック
}
}
この実装では、
エラーが発生した場合に throw e; で明示的にスローし、全体をロールバック できます。
適用ケース: データの整合性を重視する場合
3. エラーのあるレコードのみ除外して処理を続行 (部分的エラー処理)
3.1. try-catch を使用して問題のあるレコードだけ処理をスキップ
エラーが発生したレコードだけスキップし、他のレコードは処理を続ける 方法です。
✅ OK 例: ErrorLogService を利用したエラーハンドリング
public class AccountTriggerHandler {
public static void beforeInsert(List<Account> newAccounts) {
List<Account> validAccounts = new List<Account>();
for (Account acc : newAccounts) {
try {
if (String.isEmpty(acc.Name)) {
throw new CustomTriggerException('アカウント名が空です');
}
acc.Name = acc.Name.toUpperCase();
validAccounts.add(acc);
} catch (CustomTriggerException e) {
ErrorLogService.logError(e, 'AccountTriggerHandler', 'beforeInsert');
}
}
if (!validAccounts.isEmpty()) {
insert validAccounts;
}
}
}
このコードでは、
- 正常なレコードだけ
insertする - エラーのあるレコードはスキップし、エラーログに記録
適用ケース:
エラーを許容しつつ、可能な限り処理を進めたい場合
4. addError() でユーザーにエラーを通知する
addError() を使うと、エラーメッセージを直接レコードに追加し、処理を中断 できます。
ロールバックされるが、どのレコードに問題があったか UI で確認できるのが利点 です。
✅ OK 例: addError() を使用
public class AccountTriggerHandler {
public static void beforeInsert(List<Account> newAccounts) {
for (Account acc : newAccounts) {
if (String.isEmpty(acc.Name)) {
acc.addError('アカウント名は必須です'); // エラーメッセージをレコードに追加
}
}
}
}
これにより、
エラー発生時に Salesforce の UI でエラーメッセージが表示されるため、ユーザーが問題を認識しやすくなります。
適用ケース:
エラーの詳細をユーザーに伝えたい場合
5. エラーリカバリ戦略
エラーが発生した際の対応方法を整理すると、以下のようになります。
| リカバリ方法 | 適用ケース | 実装方法 |
|---|---|---|
| ロールバック | データ整合性が最優先 | throw new Exception(); |
| エラーを記録しつつ続行 | 一部エラーは許容し、できるだけ処理を進める |
ErrorLogService.logError() を利用 |
| ユーザーにエラーメッセージを通知 | データ入力者にフィードバックが必要 |
addError() を使用 |
まとめ
今回は Apex トリガーのエラーハンドリングとリカバリ方法 を解説しました。
- トリガー内で try-catch を使わず、ハンドラクラスで処理
throw e;を使い、エラー発生時に全体をロールバック- エラーのあるレコードだけ除外し、可能な限り処理を続行
addError()を使ってユーザーにフィードバック
次の Part 9 では、バッチ処理 (Batch Apex) におけるエラーハンドリング について解説します!