SOQL(Salesforce Object Query Language)是在Salesforce平台上进行数据检索的关键工具,但是如果不正确使用,可能会导致各种问题:
Governor Limits(触发限制)
在Salesforce Apex中,有一系列的执行限制,通常称为Governor Limits。对于SOQL查询,例如,一个同步事务中最多只能执行100个SOQL查询。
错误例子:
假设你有一个包含101个Account记录的列表,每个记录都需要检索相关的Contact记录。
List<Account> accounts = [SELECT Id FROM Account LIMIT 101];
for (Account a : accounts) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :a.Id];
// ...其他操作
}
在这个例子中,由于每个Account执行一次SOQL查询,当循环到第101次时,将触发Governor Limits,导致程序中断。
参考:Error: System.LimitException: Too many SOQL queries: 101
如何避免:
通过Bulkification(批量化处理)的技术,你可以避免这个问题。
List<Account> accounts = [SELECT Id FROM Account LIMIT 101];
Map<Id, List<Contact>> accountIdToContacts = new Map<Id, List<Contact>>();
List<Contact> allContacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accounts];
for (Contact c : allContacts) {
if (!accountIdToContacts.containsKey(c.AccountId)) {
accountIdToContacts.put(c.AccountId, new List<Contact>());
}
accountIdToContacts.get(c.AccountId).add(c);
}
在这个修正的例子中,我们首先从所有相关的Account记录中检索Contact记录,并存储在一个Map中。然后,在需要的地方直接从Map中取出相关的Contact列表。这样,不管有多少Account记录,都只需要一次SOQL查询。这避免了触发Governor Limits。
3.2触发器(Trigger)的不当使用
Salesforce 触发器是非常强大的工具,能够在数据库操作(如插入、更新、删除等)之前或之后执行自定义逻辑。然而,如果不当使用,它们可能导致一系列问题。本文将探讨几个常见的触发器使用失误。
3.2.1. 混合业务逻辑(Mixed Business Logic)
问题描述:
将不同的业务逻辑混合在一个触发器中可能导致代码难以维护和理解。
示例:
trigger MixedBusinessLogicTrigger on Account (before insert) {
for(Account acc : Trigger.new) {
// 业务逻辑 A
acc.Name = acc.Name.toUpperCase();
// 业务逻辑 B
if(acc.AnnualRevenue > 1000000) {
acc.HighValueAccount__c = true;
}
}
}
建议:
将不同的业务逻辑分解到不同的类或者方法中,并通过触发器调用它们。
public class AccountTriggerHandler {
public static void handleNameChange(List<Account> accs) {
for(Account acc : accs) {
acc.Name = acc.Name.toUpperCase();
}
}
public static void identifyHighValueAccounts(List<Account> accs) {
for(Account acc : accs) {
if(acc.AnnualRevenue > 1000000) {
acc.HighValueAccount__c = true;
}
}
}
}
trigger AccountTrigger on Account (before insert) {
AccountTriggerHandler.handleNameChange(Trigger.new);
AccountTriggerHandler.identifyHighValueAccounts(Trigger.new);
}
参考1:【探索Apex开发之】 使用TriggerHandler,让你的代码逻辑更清晰
参考2:【探索Apex开发之】 使用TriggerHandler,让你的代码逻辑更清晰(使用Helper类)
3.2.2. 递归触发(Recursive Triggers)
问题描述:
一个触发器调用自身或通过某种间接方式造成递归,可能导致性能问题或超出 governor limits。
示例:
假设我们有一个在Account更新时触发的触发器,该触发器又更新了Account。
trigger RecursiveTrigger on Account (after update) {
List<Account> accountsToUpdate = new List<Account>();
for(Account acc : Trigger.new) {
acc.Name = acc.Name + '!';
accountsToUpdate.add(acc);
}
update accountsToUpdate; // 递归触发
}
建议:
使用静态变量来检测和防止递归。
public class TriggerRecursionCheck {
public static Boolean isAlreadyRun = false;
}
trigger PreventRecursiveTrigger on Account (after update) {
if(TriggerRecursionCheck.isAlreadyRun) {
return;
}
TriggerRecursionCheck.isAlreadyRun = true;
// 触发器逻辑
}
3.2.3. 没有批量化处理(Lack of Bulkification)
如果触发器没有针对批量记录进行优化,可能会导致性能问题或超出 governor limits。
参考本章内容3.1的后半段。
3.3不良的Apex编程习惯