学习目标
完成本单元后,您将能够:
- 编写一个Salesforce对象的触发器。
- 使用触发器上下文变量
- 从触发器调用类方法。
- 在触发器中使用sObject addError()方法来限制保存操作。
编写 Apex触发器
Apex触发器使您能够在事件之前或之后对Salesforce中的记录(例如插入,更新或删除)执行自定义操作。就像数据库系统支持触发器一样,Apex提供触发器支持来管理记录。
通常,您可以使用触发器根据特定条件执行操作,修改相关记录或限制某些操作的发生。您可以使用触发器在Apex中执行任何操作,包括执行SOQL和DML或调用自定义的Apex方法。
使用触发器执行无法使用Salesforce用户界面中的指向点击工具完成的任务。例如,如果验证字段值或更新记录中的字段,请改为使用验证规则和工作流程规则。
可以为顶级标准对象定义触发器,例如帐户或联系人,自定义对象和一些标准子对象。触发器在创建时默认处于活动状态。当指定的数据库事件发生时,Salesforce会自动触发活动触发器。
Trigger 语法
触发器定义的语法不同于类定义的语法。触发器定义以trigger关键字开始。之后是触发器的名称,触发器所关联的Salesforce对象以及触发的条件。触发器具有以下语法:
trigger TriggerName on ObjectName (trigger_events) {
code_block
}
- before insert
- before update
- before delete
- after insert
- after update
- after delete
- after undelete
例
在插入帐户并将消息写入调试日志之前,触发这个简单的触发器。
- 在开发者控制台中,点击 .
- 输入HelloWorldTrigger作为触发器名称,然后选择sObject的Account。点击 Submit.
- 用下面的代码替换默认的代码。
trigger HelloWorldTrigger on Account (before insert) { System.debug('Hello World!'); }
- 要保存,请按Ctrl + S。
- 要测试触发器,请创建一个帐户。
- 单击 .
- 在新窗口中,添加以下内容,然后单击 Execute.
Account a = new Account(Name='Test Trigger'); insert a;
- 在调试日志中,找到Hello World!声明。日志还显示触发器已被执行。
触发器的类型
有两种类型的触发器。
- 触发器在保存到数据库之前用于更新或验证记录值之前。
- 使用触发器访问由系统设置的字段值(例如记录的Id或LastModifiedDate字段),并影响其他记录中的更改。触发后触发器的记录是只读的。
使用上下文变量
要访问导致触发器触发的记录,请使用上下文变量。例如,Trigger.New包含插入或更新触发器中插入的所有记录。 Trigger.Old在更新触发器中更新之前提供旧版本的sObjects,或在删除触发器中删除sObjects的列表。当插入一个记录时,或者通过API或Apex批量插入大量记录时,触发器可以触发。因此,上下文变量(如Trigger.New)只能包含一条记录或多条记录。您可以遍历Trigger.New来获取每个单独的sObject。
这个例子是HelloWorldTrigger示例触发器的修改版本。它遍历for循环中的每个帐户并更新每个帐户的说明字段。
trigger HelloWorldTrigger on Account (before insert) {
for(Account a : Trigger.New) {
a.Description = 'New description';
}
}
trigger ContextExampleTrigger on Account (before insert, after insert, after delete) {
if (Trigger.isInsert) {
if (Trigger.isBefore) {
// Process before insert
} else if (Trigger.isAfter) {
// Process after insert
}
}
else if (Trigger.isDelete) {
// Process after delete
}
}
触发上下文变量
下表是可用于触发器的所有上下文变量的综合列表。
变量 | 用法 |
---|---|
isExecuting | 如果Apex代码的当前上下文是触发器,而不是Visualforce页面,Web服务或executeanonymous()API调用,则返回true。 |
isInsert | 如果由于Salesforce用户界面,Apex或API的插入操作而触发此触发器,则返回true。 |
isUpdate | 如果由于Salesforce用户界面,Apex或API的更新操作而触发此触发器,则返回true。 |
isDelete | 如果由于Salesforce用户界面,Apex或API的删除操作而触发此触发器,则返回true。 |
isBefore | 如果在保存任何记录之前触发此触发器,则返回true。 |
isAfter | 如果在保存所有记录后触发此触发器,则返回true。 |
isUndelete | 如果在从回收站中恢复记录(即从Salesforce用户界面,Apex或API取消删除操作之后)触发此触发器,则返回true。 |
new | 返回sObject记录的新版本列表。 此sObject列表仅在插入,更新和取消删除触发器中可用,并且记录只能在before触发器中修改。 |
newMap | ID对新对象记录的版本的映射。 此映射仅在更新之前,插入之后,更新之后和取消删除触发器之后可用。 |
old | 返回sObject记录的旧版本列表。 该sObject列表仅在更新和删除触发器中可用。 |
oldMap | 旧版本的sObject记录的ID映射。 此映射仅在更新和删除触发器中可用。 |
size | 触发器调用中的记录总数,包括旧的和新的。 |
从触发器调用类方法
您可以通过触发器调用公用事业方法。调用其他类的方法可以重用代码,减少触发器的大小,并且可以提高Apex代码的维护。它也允许你使用面向对象的编程。
以下示例触发器显示如何从触发器调用静态方法。如果由于插入事件触发了该触发器,则此示例将在EmailManager类上调用静态sendMail()方法。此实用程序方法向指定的收件人发送电子邮件,并包含插入的联系人记录数。
- 在开发者控制台中,点击 .
- 输入ExampleTrigger作为触发器名称,然后选择Contact作为sObject。点击 Submit.
- 将默认代码替换为以下内容,然后将sendMail()中的电子邮件地址占位符文本修改为您的电子邮件地址。
trigger ExampleTrigger on Contact (after insert, after delete) { if (Trigger.isInsert) { Integer recordCount = Trigger.New.size(); // 从另一个类调用实用程序方法 EmailManager.sendMail('Your email address', 'Trailhead Trigger Tutorial', recordCount + ' contact(s) were inserted.'); } else if (Trigger.isDelete) { // 删除后的处理 } }
- 要保存,请按Ctrl + S。
- 要测试触发器,请创建一个联系人。
- 单击 .
- 在新窗口中,添加以下内容,然后单击 Execute.
Contact c = new Contact(LastName='Test Contact'); insert c;
- 在调试日志中,检查触发器是否被触发。在日志末尾,找到实用程序方法编写的调试消息: DEBUG|Email sent successfully
- 现在检查您是否收到一封电子邮件,其中正文 1 contact(s) were inserted.
使用新的触发器,每次添加一个或多个联系人时都会收到一封电子邮件!
添加相关记录
触发器通常用于访问和管理与触发器上下文中的记录相关的记录 – 触发该触发器的记录。
如果没有机会与客户相关联,则此触发器为每个新的或更新的客户添加相关机会。触发器首先执行SOQL查询,以获取触发器触发的帐户的所有子机会。接下来,触发器遍历Trigger.New中的sObjects列表以获取每个帐户的sObject。如果该帐户没有任何相关的机会sObjects,for循环会创建一个。如果触发器创造了新的机会,最后的陈述将插入它们。
- 使用开发者控制台添加以下触发器(按照HelloWorldTrigger示例的步骤,但使用AddRelatedRecord作为触发器名称)。
trigger AddRelatedRecord on Account(after insert, after update) { List<Opportunity> oppList = new List<Opportunity>(); // 获取这个触发器中客户的相关机会 Map<Id,Account> acctsWithOpps = new Map<Id,Account>( [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.New]); // 为每个帐户添加一个机会,如果它还没有。 // 遍历每个帐户。 for(Account a : Trigger.New) { System.debug('acctsWithOpps.get(a.Id).Opportunities.size()=' + acctsWithOpps.get(a.Id).Opportunities.size()); // 检查帐号是否有相关的机会。 if (acctsWithOpps.get(a.Id).Opportunities.size() == 0) { // 如果没有,请添加一个默认机会 oppList.add(new Opportunity(Name=a.Name + ' Opportunity', StageName='Prospecting', CloseDate=System.today().addMonths(1), AccountId=a.Id)); } } if (oppList.size() > 0) { insert oppList; } }
- 要测试触发器,请在Salesforce用户界面中创建一个帐户,并将其命名为Apple和Orange。
- 在客户页面的机会相关列表中,找到新的机会。触发器自动添加了这个机会!
超越基础
你添加的触发器迭代所有属于触发器上下文的记录 – for循环遍历Trigger.New。但是,这个触发器中的循环可能更有效率。我们并不是真的需要访问这个触发器上下文中的每个客户,而是只有一个子集 – 没有机会的客户。下一个单元显示如何使这个触发器更有效率。在“批量触发器设计模式”单元中,了解如何修改SOQL查询以仅获取没有机会的帐户。然后,学习只遍历这些记录。
使用触发器异常
您有时需要对某些数据库操作添加限制,例如在满足某些条件时防止保存记录。为了防止在触发器中保存记录,在有问题的sObject上调用addError()方法。 addError()方法在触发器中引发致命错误。错误消息显示在用户界面中并被记录。
如果具有相关机会,则以下触发器可防止删除帐户。默认情况下,删除一个帐户会导致所有相关记录的级联删除。这个触发器可以防止级联删除机会。为自己尝试这个触发器!如果你已经执行了前面的例子,你的组织有一个名为苹果和橙子的客户有一个相关的机会。本示例使用该示例帐户。
- 使用开发者控制台添加以下触发器。
trigger AccountDeletion on Account (before delete) { // 如果他们有相关的机会,防止删除帐户。 for (Account a : [SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM Opportunity) AND Id IN :Trigger.old]) { Trigger.oldMap.get(a.Id).addError( '不能删除有相关机会的帐号'); } }
- 在Salesforce用户界面中,导航到Apples&Oranges帐户的页面,然后单击 Delete.
- 在确认弹出窗口中,单击 OK.
使用自定义错误消息查找验证错误无法删除具有相关机会的帐户。
- 禁用AccountDeletion触发器。如果你保持这个触发器激活,你不能检查你的挑战。
- 从设置中搜索Apex触发器。
- 在Apex触发器页面上,单击AccountDeletion触发器旁边的编辑。
- 取消选择 Is Active.
- 点击 Save.
超越基础
在触发器中调用addError()会导致整个操作集回滚,除非部分成功调用批量DML。
- 如果Apex中的DML语句产生了触发器,则任何错误都会回滚整个操作。但是,运行时引擎仍会处理操作中的每个记录,以编译一个全面的错误列表。
- 如果Force.com API中的批量DML调用产生了触发器,那么运行时引擎将把有害的记录放在一边。运行时引擎然后尝试部分保存没有产生错误的记录。
触发器和外调
Apex允许您打电话并将Apex代码与外部Web服务集成。 Apex调用外部Web服务称为标注。例如,您可以调出股票报价服务以获取最新的报价。当从触发器中进行标注时,标注必须异步完成,以便在等待外部服务的响应时触发器不会阻止您的工作。异步标注在后台进程中进行,并在收到响应时外部服务返回它。
要从触发器中进行调用,请调用异步执行的类方法。这种方法被称为未来的方法,并用@future(callout = true)进行注释。此示例类包含制作标注的未来方法。
public class CalloutClass {
@future(callout=true)
public static void makeCallout() {
HttpRequest request = new HttpRequest();
// 设置端点URL。
String endpoint = 'http://yourHost/yourService';
request.setEndPoint(endpoint);
// 将HTTP动词设置为GET。
request.setMethod('GET');
// 发送HTTP请求并获得响应。
HttpResponse response = new HTTP().send(request);
}
}
这个例子展示了调用类中的方法来异步调用标注的触发器。
trigger CalloutTrigger on Account (before insert, before update) {
CalloutClass.makeCallout();
}