Part 5: ガバナ制限を考慮したエラーハンドリングとバッチ処理の例外処理
前回は エラーログの記録、ユーザー通知、トランザクション管理 について解説しました。
今回は ガバナ制限を考慮したエラーハンドリング と バッチ処理の例外処理 について詳しく解説します。
1. ガバナ制限を考慮したエラーハンドリング
Salesforce では、Apex の実行時に「ガバナ制限 (Governor Limits)」が適用されます。
特に LimitException
は try-catch
でキャッチできないため、事前対策が重要 です。
1.1. LimitException の代表例
以下のような制限を超えると LimitException
が発生し、処理が強制終了します。
ガバナ制限 | 説明 |
---|---|
SOQL 100 クエリ制限 |
1 トランザクションで SOQL を 100 回以上実行するとエラー |
DML 150 操作制限 |
insert , update , delete などを 150 回以上実行するとエラー |
CPU 時間制限 |
処理に 10 秒以上かかるとエラー |
ヒープサイズ制限 |
メモリ使用量が 6MB (標準) または 12MB (バッチ) を超えるとエラー |
1.2. Limits
クラスを使った制限の事前チェック
ガバナ制限を回避するために、Limits
クラスを使って現在の使用状況を確認できます。
if (Limits.getQueries() >= Limits.getLimitQueries() - 5) {
System.debug('SOQL クエリ制限に近づいています');
}
if (Limits.getDmlStatements() >= Limits.getLimitDmlStatements() - 10) {
System.debug('DML 操作制限に近づいています');
}
Limits.getQueries()
で現在の SOQL 実行回数を取得し、
Limits.getLimitQueries()
で上限(100)を取得できます。
1.3. SOQL forループ
を使ってクエリ制限を回避
通常の for
ループで SOQL
を使うと、100 クエリ制限を超える可能性があります。
NG 例:
for (Account acc : [SELECT Id, Name FROM Account]) {
Contact con = [SELECT Id FROM Contact WHERE AccountId = :acc.Id LIMIT 1]; // クエリがループごとに実行される
}
この方法だと アカウントが 100 件以上あると SOQL 100 回制限
を超えてエラーになります。
OK 例:
Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name FROM Account]);
List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountMap.keySet()];
この方法なら SOQL 実行回数は 2 回に抑えられます。
1.4. Database.insert()
を使って DML 操作制限を回避
insert
をループの中で実行すると、DML 150 回制限を超える可能性 があります。
NG 例:
for (Account acc : accounts) {
insert acc; // 150 件以上のアカウントを処理するとエラー
}
OK 例:
insert accounts; // 一括 DML で 1 回の操作で済む
2. バッチ処理 (Batch Apex) の例外処理
バッチ処理では、数千~数百万件のレコードを処理することがあるため、
エラーハンドリングの戦略が重要 になります。
2.1. バッチで try-catch
を使う
バッチ実行中にエラーが発生すると、そのバッチ内の全レコードがロールバック されます。
これを防ぐには try-catch
を使って エラーになったレコードだけをスキップ します。
NG 例:
global void execute(Database.BatchableContext bc, List<Account> scope) {
insert scope; // 1件でもエラーがあると全件ロールバックされる
}
OK 例:
global void execute(Database.BatchableContext bc, List<Account> scope) {
List<Account> successList = new List<Account>();
for (Account acc : scope) {
try {
insert acc;
successList.add(acc);
} catch (DmlException e) {
System.debug('エラー: ' + e.getMessage());
}
}
}
こうすることで、エラーになったレコードだけをスキップし、成功したレコードはそのまま処理 できます。
2.2. Database.insert()
でエラーを個別処理
Database.insert()
の第二引数に false
を指定すると、部分成功が可能 になります。
global void execute(Database.BatchableContext bc, List<Account> scope) {
Database.SaveResult[] results = Database.insert(scope, false);
for (Database.SaveResult sr : results) {
if (!sr.isSuccess()) {
for (Database.Error err : sr.getErrors()) {
System.debug('エラー: ' + err.getMessage());
}
}
}
}
この方法では、エラーが発生したレコードだけを特定し、他のレコードは正常に処理 されます。
2.3. バッチのエラーを finish()
でログに記録
バッチ実行後にエラーログを記録するには finish()
メソッドを活用します。
global void finish(Database.BatchableContext bc) {
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setSubject('バッチ処理完了');
mail.setPlainTextBody('バッチ処理が完了しました。エラーログを確認してください。');
mail.setToAddresses(new String[] {'admin@example.com'});
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
このように finish()
で バッチ完了時にメール通知 することで、エラー発生時にすぐ気付けます。
3. 例外処理のベストプラクティス
-
ガバナ制限を意識する
-
SOQL forループ
を避ける -
Limits
クラスを使って事前チェック - 一括
DML
(insert list
) を活用
-
-
バッチ処理では try-catch を使う
- 1 件のエラーで全件ロールバックされないようにする
-
バッチのエラーを
finish()
で記録・通知する- エラーログを作成し、管理者に通知
まとめ
今回は、ガバナ制限を考慮したエラーハンドリング と バッチ処理のエラーハンドリング について解説しました。
-
SOQL 100 回制限
やDML 150 回制限
を 事前チェック で回避 -
try-catch
を使って バッチ処理でのロールバックを防ぐ -
Database.insert(scope, false)
で 部分成功を許可 -
finish()
で エラーログを記録し、通知する
次の Part 6 では、テストクラスでのエラーハンドリング や カスタム例外の活用 について解説します!