Apex 触发器入门教程
学习目标
完成本单元后,您将能够:
- 为 Salesforce 对象编写触发器。
- 使用触发器上下文变量。
- 从触发器中调用类方法。
- 在触发器中使用 sObject
addError()
方法限制保存操作。
开始之前
Apex 触发器有用、有趣且时髦。此模块不仅可帮助您开始使用它们,还引用了其他 Salesforce 功能来向您展示 Apex 触发器的强大功能。为了充分利用此模块,我们强烈建议您先查看以下模块:
编写 Apex 触发器
Apex 触发器使您能够在事件之前或之后对 Salesforce 中的记录执行自定义操作,例如添加、更新或删除。就像数据库系统支持触发器一样,Apex 为管理记录也提供了触发器支持。
通常,您根据特定条件使用触发器来执行某些操作、修改相关记录或限制某些操作的发生。您可以使用触发器来执行 Apex 中支持的任何操作,包括执行 SOQL 和 DML 或调用自定义 Apex 方法。
使用触发器来执行通过 Salesforce 用户界面中的单击式工具无法完成的任务。例如,如果要验证字段值或更新记录中的字段,请用验证规则和流。如果性能和规模很重要,或者逻辑对于点击式工具来说过于复杂,或者正在执行 CPU 密集型操作,请使用 Apex 触发器。
为顶级标准对象,例如客户或联系人、自定义对象和一些标准子对象定义触发器。触发器在创建时默认处于有效状态。当发生指定数据库事件时,Salesforce 自动触发有效触发器。
触发器语法
触发器定义的语法与类定义的语法不同。触发器定义以 trigger
关键字开头。然后是触发器的名称、触发器关联的 Salesforce 对象以及触发条件。触发器包含以下语法:
trigger TriggerName on ObjectName (trigger_events) { code_block }
要在插入、更新、删除和取消删除操作之前或之后执行触发器,请在逗号分隔列表中指定多个触发器事件。可指定事件如下:
before insert
before update
before delete
after insert
after update
after delete
after undelete
触发器示例
在您插入客户并将消息写入调试日志之前,触发了这个简单的触发器。
- 在 Developer Console 中,单击 File(文件) | New(新建) | Apex Trigger(Apex 触发器)。
- 输入
HelloWorldTrigger
作为触发器名称,然后为 sObject 选择 Account(客户)。单击 Submit(提交)。 - 将默认代码替换为以下内容。
trigger HelloWorldTrigger on Account (before insert) { System.debug('Hello World!'); }
- 按 Ctrl+S 进行保存。
- 要测试触发器,请创建一个客户。
- 单击 Debug(调试) | Execute Anonymous Window(打开执行匿名窗口)。
- 在新窗口中,添加以下内容,然后单击 Execute(执行)。
Account a = new Account(Name='Test Trigger'); insert a;
- 在调试日志中,找到
Hello World!
语句。日志还会显示触发器已执行。
触发器类型
共有两种触发器类型。
- Before 触发器通常用于在记录被保存到数据库之前更新或者校验记录值。
- After 触发器用于访问系统设置的字段值(例如记录的
Id
或者LastModifiedDate
字段),并影响其他记录中的更改。触发 after 触发器的记录为只读。
使用上下文变量
要访问导致触发器触发的记录,请使用上下文变量。例如,Trigger.new
包含 insert 或 update 触发器中插入的所有记录。Trigger.old
提供在 update 触发器中更新之前的旧版本 sObject,或 delete 触发器中已删除的 sObject 列表。当插入一条记录或通过 API 或 Apex 批量插入记录时,会触发触发器。因此,诸如 Trigger.new
之类的上下文变量只能包含一条或多条记录。您可以遍历 Trigger.new
来获取每个独立的 sObject。
此示例是 HelloWorldTrigger
示例触发器的修改版。它在 for 循环中遍历每个客户并更新每个客户的Description
(描述)字段。
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 服务或 |
isInsert | 如果此触发器由于插入操作,并从 Salesforce 用户界面、Apex 或 API 触发,则返回 |
isUpdate | 如果此触发器由于更新操作,并从 Salesforce 用户界面、Apex 或 API 触发,则返回 |
isDelete | 如果此触发器由于删除操作,并从 Salesforce 用户界面、Apex 或 API 触发,则返回 |
isBefore | 如果在保存任何记录之前触发此触发器,则返回 |
isAfter | 如果在保存任何记录之后触发此触发器,则返回 |
isUndelete | 如果在从回收站恢复记录后触发此触发器,则返回 |
new | 返回 sObject 记录的新版本列表。 此 sObject 列表仅在 |
newMap | ID 到 sObject 新版本记录的映射。 此映射仅在 |
old | 返回 sObject 记录的旧版本列表。 此 sObject 列表仅在 |
oldMap | ID 到 sObject 旧版本记录的映射。 此映射仅在 |
operationType | 返回与当前操作对应的 System.TriggerOperation 类型的枚举。
|
size | 触发器调用中包含的新旧记录总数。 |
从触发器中调用类方法
您可以从触发器中调用公共实用程序方法。调用其他类的方法可实现代码重用、减少触发器的大小并改进 Apex 代码的维护。还允许您使用面向对象的编程。
以下示例展示了如何从触发器调用静态方法。如果触发器因插入事件而被触发,该示例将调用 EmailManager
类上的静态 sendMail()
方法。该实用程序方法向指定收件人发送电子邮件,并包含插入的联系人记录数。
- 在 Developer Console 中,单击 File(文件)| New(新建)| Apex Class(Apex 类)。
- 输入
EmailManager
,然后单击 OK(确定)。 - 用下面的
EmailManager
类示例替换默认类主体。public class EmailManager { // Public method public static void sendMail(String address, String subject, String body) { // Create an email message object Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {address}; mail.setToAddresses(toAddresses); mail.setSubject(subject); mail.setPlainTextBody(body); // Pass this email message to the built-in sendEmail method // of the Messaging class Messaging.SendEmailResult[] results = Messaging.sendEmail( new Messaging.SingleEmailMessage[] { mail }); // Call a helper method to inspect the returned results inspectResults(results); } // Helper method private static Boolean inspectResults(Messaging.SendEmailResult[] results) { Boolean sendResult = true; // sendEmail returns an array of result objects. // Iterate through the list to inspect results. // In this class, the methods send only one email, // so we should have only one result. for (Messaging.SendEmailResult res : results) { if (res.isSuccess()) { System.debug('Email sent successfully'); } else { sendResult = false; System.debug('The following errors occurred: ' + res.getErrors()); } } return sendResult; } }
- 在 Developer Console 中,单击 File(文件) | New(新建) | Apex Trigger(Apex 触发器)。
- 输入
ExampleTrigger
作为触发器名称,然后为 sObject 选择 Contact(联系人)。单击 Submit(提交)。 - 将默认代码替换为以下内容,然后将
sendMail()
中的电子邮件地址占位符文本修改为您的电子邮件地址。trigger ExampleTrigger on Contact (after insert, after delete) { if (Trigger.isInsert) { Integer recordCount = Trigger.new.size(); // Call a utility method from another class EmailManager.sendMail('Your email address', 'Trailhead Trigger Tutorial', recordCount + ' contact(s) were inserted.'); } else if (Trigger.isDelete) { // Process after delete } }
- 按 Ctrl+S 进行保存。
- 要测试触发器,请创建一个联系人。
- 单击 Debug(调试) | Execute Anonymous Window(打开执行匿名窗口)。
- 在新窗口中,添加以下内容,然后单击 Execute(执行)。
Contact c = new Contact(LastName='Test Contact'); insert c;
- 在调试日志中,检查触发器是否被触发。在日志末尾,找到由实用程序方法写入的调试消息:
DEBUG|Email sent successfully
- 现在检查您是否收到一封电子邮件,正文文本内容为
1 contact(s) were inserted
(已插入 1 个联系人)。
有了新的触发器,每次添加一个或多个联系人时您都会收到一封电子邮件!
添加相关记录
触发器通常用于访问和管理与触发器上下文中的记录相关的记录 — 即导致此触发器触发的记录。
如果客户没有关联的业务机会,则该触发器将为每个新增或更新的客户添加一个业务机会。首先,触发器会执行 SOQL 查询以获取触发器触发的客户的所有子业务机会。然后,触发器遍历 Trigger.new
中的 sObject 列表以获取每个客户的 sObject。如果客户没有关联的业务机会 sObject,则 for 循环会为其创建一个 sObject。如果触发器创建了任意业务机会,则最终执行的语句会将其插入进来。
- 使用 Developer Console 添加以下触发器(遵循
HelloWorldTrigger
示例中的步骤,但要使用AddRelatedRecord
作为触发器名称)。trigger AddRelatedRecord on Account(after insert, after update) { List<Opportunity> oppList = new List<Opportunity>(); // Get the related opportunities for the accounts in this trigger Map<Id,Account> acctsWithOpps = new Map<Id,Account>( [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.new]); // Add an opportunity for each account if it doesn't already have one. // Iterate through each account. for(Account a : Trigger.new) { System.debug('acctsWithOpps.get(a.Id).Opportunities.size()=' + acctsWithOpps.get(a.Id).Opportunities.size()); // Check if the account already has a related opportunity. if (acctsWithOpps.get(a.Id).Opportunities.size() == 0) { // If it doesn't, add a default opportunity 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 用户界面中创建一个客户并将其命名为
Apples & Oranges
。 - 在客户页面的业务机会相关列表中,找到新创建的业务机会。触发器自动添加了该业务机会!
使用触发器异常
您有时需要对某些数据库操作添加限制,例如在满足某些条件时防止保存记录。要防止在触发器中保存记录,请对相关 sObject 调用 addError()
方法。addError()
方法在触发器内部抛出一个致命错误。用户界面将显示并记录该错误提示。
以下触发器可防止删除具有相关业务机会的客户。默认情况下,删除客户会引起其所有关联记录的级联删除。该触发器可防止业务机会的级联删除操作。您试试这款触发器吧!如果您执行了上一个示例,组织会有一个名为 Apples & Oranges
的客户,还包含了关联的业务机会。本示例中使用了示例客户。
- 通过 Developer Console 添加以下触发器。
trigger AccountDeletion on Account (before delete) { // Prevent the deletion of accounts if they have related opportunities. 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( 'Cannot delete account with related opportunities.'); } }
- 在 Salesforce 用户界面,导航至
Apples & Oranges
客户页面,然后单击 Delete(删除)。 - 在确认弹出窗口中,单击 OK(确定)。找到显示自定义错误提示消息。
找到显示自定义错误提示消息Cannot delete account with related opportunities
(无法删除拥有关联业务机会的客户)的验证错误。
- 禁用
AccountDeletion
触发器。如果该触发器处于有效状态,则无法检查错误。
- 在 Setup(设置)中,搜索
Apex Triggers
(Apex 触发器)。 - 在 Apex Triggers(Apex 触发器)页面中,单击
AccountDeletion
触发器旁边的 Edit(编辑)。 - 取消选择 Is Active(有效)。
- 单击保存。
触发器和调出
Apex 允许您调用 Apex 代码并将其与外部 Web 服务集成。对外部 Web 服务的 Apex 调用称之为调出。例如,您可以调出股票报价服务以获取最新报价。当从触发器发起调出时,必须异步执行,以便触发器进程不会在等待外部服务响应时阻止您的工作。异步调出在后台进程中进行,当外部服务返回结果时接收到响应。
要从触发器发起调出,请使用异步执行的类方法。这个类方法被称为 future 方法,并用 @future(callout=true)
注释。示例类中包含发起调出的 future 方法。
public class CalloutClass { @future(callout=true) public static void makeCallout() { HttpRequest request = new HttpRequest(); // Set the endpoint URL. String endpoint = 'http://yourHost/yourService'; request.setEndPoint(endpoint); // Set the HTTP verb to GET. request.setMethod('GET'); // Send the HTTP request and get the response. HttpResponse response = new HTTP().send(request); } }
本示例展示了调用类中的方法以异步调出触发器。
trigger CalloutTrigger on Account (before insert, before update) { CalloutClass.makeCallout(); }
本章节仅提供关于调出的简要介绍,不作深入探讨。有关更多信息,请参见 Apex 开发人员指南中的使用 Apex 发起调出。
资源