Apex触发器单条正常批量触发超Governor Limits问题排查与优化
问题:Apex触发器批量处理失败的原因与重构方案
我是一名Salesforce开发者,正在开发基于关联Account数据更新Contact记录的Apex触发器。该触发器在UI中逐条创建或更新记录时表现正常,但批量处理(如Data Loader或API)时失败。
触发器简化代码
trigger ContactTrigger on Contact (after insert, after update) { for (Contact c : Trigger.new) { if (c.AccountId == null) { continue; } Account acc = [ SELECT Id, Industry FROM Account WHERE Id = :c.AccountId ]; if (acc.Industry == 'IT') { c.Description = 'IT Account'; update c; } } }
批量操作时的错误信息
System.LimitException: Too many SOQL queries: 101System.LimitException: Too many DML statements: 151
已尝试的方案
- 添加空值检查与循环内条件逻辑
- 限制触发器仅在所需上下文运行
- 确认无Flows或Process Builder引发的递归
但批量操作问题仍持续存在。
为什么仅批量处理时失败?
Salesforce有严格的Governor Limits(运行时限制):单事务内最多允许100次SOQL查询、150次DML操作。
- 单条记录操作时,触发器仅执行1次SOQL和1次DML,远低于限制,所以正常运行。
- 批量操作时,原代码在循环中对每条Contact单独执行SOQL查询和DML更新。比如批量处理100条Contact,就会触发100次SOQL和100次DML;若批量数超过100,直接触发SOQL次数超限;DML次数超过150也会触发报错。
重构方案(符合批量处理规范)
遵循Salesforce Apex最佳实践:批量查询、批量更新,绝对避免在循环内执行SOQL和DML。
步骤1:创建触发器Handler类(分离逻辑,便于维护)
public class ContactTriggerHandler { // 静态变量防止递归更新 private static Boolean isRecursive = false; public static void handleAfterInsertUpdate(List<Contact> newContacts) { if (isRecursive) return; isRecursive = true; // 1. 收集所有非空的AccountId并去重 Set<Id> accountIds = new Set<Id>(); for (Contact c : newContacts) { if (c.AccountId != null) { accountIds.add(c.AccountId); } } // 2. 批量查询关联的Account数据 Map<Id, Account> accountMap = new Map<Id, Account>([ SELECT Id, Industry FROM Account WHERE Id IN :accountIds ]); // 3. 收集需要更新的Contact记录 List<Contact> contactsToUpdate = new List<Contact>(); for (Contact c : newContacts) { Account acc = accountMap.get(c.AccountId); if (acc != null && acc.Industry == 'IT') { Contact updatedContact = new Contact( Id = c.Id, Description = 'IT Account' ); contactsToUpdate.add(updatedContact); } } // 4. 批量执行DML更新 if (!contactsToUpdate.isEmpty()) { update contactsToUpdate; } isRecursive = false; } }
步骤2:简化触发器,仅调用Handler
trigger ContactTrigger on Contact (after insert, after update) { ContactTriggerHandler.handleAfterInsertUpdate(Trigger.new); }
重构后的优势
- 无论批量多少条记录,仅执行1次SOQL查询和1次DML更新,完全符合Governor Limits要求。
- 用
Set<Id>去重AccountId,避免查询重复的Account数据。 - 静态变量
isRecursive防止触发器递归调用(比如更新Contact后再次触发update事件)。 - 分离触发器与业务逻辑,代码更易维护和测试。
内容的提问来源于stack exchange,提问作者bselavina
相关产品推荐
相关产品推荐

