Skip to main content

Apex 触发器入门教程

学习目标

完成本单元后,您将能够:

  • 为 Salesforce 对象编写触发器。
  • 使用触发器上下文变量。
  • 从触发器中调用类方法。
  • 在触发器中使用 sObject addError() 方法限制保存操作。
备注

备注

用中文(简体)学习?在中文(简体)Trailhead Playground 中开始挑战,用括号中提供的译文完成挑战。仅复制并粘贴英文值,因为挑战验证基于英文数据。如果在中文(简体)组织中没有成功通过挑战,我们建议您 (1) 将区域设置切换为美国,(2) 按此处说明将语言切换为英文,(3) 再次单击“检查挑战”按钮。

查看 Trailhead 本地化语言徽章详细了解如何利用 Trailhead 译文。

开始之前

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

触发器示例

在您插入客户并将消息写入调试日志之前,触发了这个简单的触发器。

  1. 在 Developer Console 中,单击 File(文件) | New(新建) | Apex Trigger(Apex 触发器)
  2. 输入 HelloWorldTrigger 作为触发器名称,然后为 sObject 选择 Account(客户)。单击 Submit(提交)
  3. 将默认代码替换为以下内容。
trigger HelloWorldTrigger on Account (before insert) {
	System.debug('Hello World!');
}
  1. Ctrl+S 进行保存。
  2. 要测试触发器,请创建一个客户。
    • 单击 Debug(调试) | Execute Anonymous Window(打开执行匿名窗口)
    • 在新窗口中,添加以下内容,然后单击 Execute(执行)
Account a = new Account(Name='Test Trigger');
insert a;
  1. 在调试日志中,找到 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';
    }
}
备注

触发器执行完毕后,系统保存 before 触发器的触发记录。您可以在不显式调用 DML 插入或更新操作的情况下修改触发器中的记录。如果对这些记录执行 DML 语句,则会出现错误。

其他部分上下文变量返回一个布尔值,以指示触发器是由于更新还是其他事件引起的触发。当触发器组合多个事件时,这些变量有很大的用处。例如:

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

如果在从回收站恢复记录后触发此触发器,则返回 true。从 Salesforce 用户界面、Apex 或 API 执行取消删除操作后,会出现恢复记录的情况。

new

返回 sObject 记录的新版本列表。

此 sObject 列表仅在 insertupdateundelete 触发器中可用,并且只能在 before 触发器中修改记录。

newMap

ID 到 sObject 新版本记录的映射。

此映射仅在 before updateafter insertafter update 以及 after undelete 触发器中可用。

old

返回 sObject 记录的旧版本列表。

此 sObject 列表仅在 updatedelete 触发器中可用。

oldMap

ID 到 sObject 旧版本记录的映射。

此映射仅在 updatedelete 触发器中可用。

operationType

返回与当前操作对应的 System.TriggerOperation 类型的枚举。

System.TriggerOperation 枚举的可能值包括:BEFORE_INSERTBEFORE_UPDATEBEFORE_DELETEAFTER_INSERTAFTER_UPDATEAFTER_DELETEAFTER_UNDELETE。如果您根据不同的触发器类型改变编程逻辑,请考虑使用 switch 语句,该语句具有独特触发器执行枚举状态的不同排列。

size

触发器调用中包含的新旧记录总数。

从触发器中调用类方法

您可以从触发器中调用公共实用程序方法。调用其他类的方法可实现代码重用、减少触发器的大小并改进 Apex 代码的维护。还允许您使用面向对象的编程。

以下示例展示了如何从触发器调用静态方法。如果触发器因插入事件而被触发,该示例将调用 CustomContactNotification 类上的静态 notifyUsers() 方法。此实用程序方法向触发器中定义的 Salesforce 用户发送自定义通知。通知包含插入的联系人记录数量。

备注

针对本例,您将创建一个自定义通知。自定义通知仅在 Lightning Experience 中可用。创建和编辑通知需要“自定义应用程序”用户权限。更多信息,请参见创建桌面或移动通知

  1. 创建桌面自定义通知。
    • 在 Setup(设置)中的 Quick Find(快速查找)框内输入 Notification Builder(通知生成器),然后选择 Custom Notifications(自定义通知)
    • 单击 New(新建)
    • 关于 Custom Notification Name(自定义通知名称),输入 New Contact Notification(新建联系人通知)。
    • 在 API Name(API Name)处输入 New_Contact_Notification
    • 对于 Supported Channels(支持的渠道),选择 Desktop(桌面)
    • 单击 Save(保存)
  2. 在 Developer Console 中,单击 File(文件)| New(新建)| Apex Class(Apex 类)
  3. 输入 CustomContactNotification,然后单击 OK(确定)
  4. 用下面的 CustomContactNotification 类示例替换默认类主体。
public with sharing class CustomContactNotification {
    // Public method
    public static void notifyUsers(Set<String> recipientsIds, Integer recordCount) {
        

        // Get the ID for the custom notification type created in Setup
 		CustomNotificationType notificationType =
            [SELECT Id, DeveloperName
             FROM CustomNotificationType
             WHERE DeveloperName='New_Contact_Notification'];
        

        // Create a new custom notification
        Messaging.CustomNotification notification = new Messaging.CustomNotification();
        

        // Set the contents for the notification
        notification.setTitle('Trailhead Trigger Tutorial');
        notification.setBody(recordCount + ' contact(s) were inserted.');


        // Set the notification type and target
        notification.setNotificationTypeId(notificationType.Id);
        // '000000000000000AAA' is a dummy targetId value
        notification.setTargetId('000000000000000AAA');
        

       // Send the notification
       try {
           notification.send(recipientsIds);
           System.debug('Custom notification sent successfully.');
       }
       catch (Exception e) {
           System.debug('Problem sending notification: ' + e.getMessage());
        }
    }
}
  1. 在 Developer Console 中,单击 File(文件) | New(新建) | Apex Trigger(Apex 触发器)
  2. 输入 ContactNotificationTrigger 作为触发器名称,然后为 sObject 选择 Contact(联系人)。单击 Submit(提交)
  3. 将默认代码替换为以下内容。
trigger ContactNotificationTrigger on Contact (after insert, after delete) {
    if (Trigger.isInsert) {
        Integer recordCount = Trigger.new.size();
        

        // Set the recipientIDs to the current user
        Set<String> recipientIDs = new Set<String>{UserInfo.getUserId()};
      

        // Call a utility method from another class
        CustomContactNotification.notifyUsers(recipientIDs, recordCount);
    }
    else if (Trigger.isDelete) {
        // Process after delete
    }
}
  1. Ctrl+S 进行保存。
  2. 要测试触发器,请创建一个联系人。
    • 单击 Debug(调试) | Execute Anonymous Window(打开执行匿名窗口)
    • 在新窗口中,添加以下内容,然后单击 Execute(执行)
Contact c = new Contact(LastName='Test Contact');
insert c;
  1. 在调试日志中,检查触发器是否被触发。在日志末尾,找到由实用程序方法写入的调试消息:DEBUG|Custom notification sent successfully.(DEBUG|自定义通知发送成功)。
  2. 现在查看您是否收到通知。要打开 Notification(通知)面板,单击 通知铃。您应该看到标题为 Trailhead Trigger Tutorial(Trailhead 触发教程)的通知,并插入正文“1 contact(s) were inserted”(已插入 1 个联系人)。

有了新的触发器,每次添加一个或多个联系人时您都会收到一个自定义桌面通知!

触发器通常用于访问和管理与触发器上下文中的记录相关的记录 — 即导致此触发器触发的记录。

如果客户没有关联的业务机会,则该触发器将为每个新增或更新的客户添加一个业务机会。首先,触发器会执行 SOQL 查询以获取触发器触发的客户的所有子业务机会。然后,触发器遍历 Trigger.new 中的 sObject 列表以获取每个客户的 sObject。如果客户没有关联的业务机会 sObject,则 for 循环会为其创建一个 sObject。如果触发器创建了任意业务机会,则最终执行的语句会将其插入进来。

  1. 使用 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;
    }
}
  1. 要测试该触发器,请在 Salesforce 用户界面中创建一个客户并将其命名为 Apples & Oranges
  2. 在客户页面的业务机会相关列表中,找到新创建的业务机会。触发器自动添加了该业务机会!
备注

您添加的触发器遍历触发器上下文的所有记录 — for 循环遍历 Trigger.new。但是,该触发器中的循环可能更有效。在此触发器上下文中,我们并不需要访问每一个客户,只需要访问没有业务机会的客户的一个子集。下一个单元将演示如何让触发器更高效。在“批量触发器设计模式”单元中,学习如何通过修改 SOQL 查询,实现仅获取没有业务机会的客户。然后,对这些记录进行遍历。

使用触发器异常

您有时需要对某些数据库操作添加限制,例如在满足某些条件时防止保存记录。要防止在触发器中保存记录,请对相关 sObject 调用 addError() 方法。addError() 方法在触发器内部抛出一个致命错误。用户界面将显示并记录该错误提示。

以下触发器可防止删除具有相关业务机会的客户。默认情况下,删除客户会引起其所有关联记录的级联删除。该触发器可防止业务机会的级联删除操作。您试试这款触发器吧!如果您执行了上一个示例,组织会有一个名为 Apples & Oranges 的客户,还包含了关联的业务机会。本示例中使用了示例客户。

  1. 通过 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.');
    }
}
  1. 在 Salesforce 用户界面,导航至 Apples & Oranges 客户页面,然后单击 Delete(删除)
  2. 在确认弹出窗口中,单击确定

通过自定义错误提示 Cannot delete account with related opportunities(无法删除拥有关联业务机会的客户)查找验证错误。

  1. 禁用 AccountDeletion 触发器。如果该触发器处于有效状态,则无法检查错误。
    • 在 Setup(设置)中,搜索 Apex Triggers(Apex 触发器)。
    • 在 Apex Triggers(Apex 触发器)页面中,单击 AccountDeletion 触发器旁边的 Edit(编辑)
    • 取消选择 Is Active(有效)
    • 单击保存
备注

除非批量 DML 调用时部分成功,否则在触发器中调用 addError() 会导致整个操作集回滚。

  • 如果 Lightning 平台 API 中的批量 DML 调用生成了触发器,则运行时引擎会将不良记录放在一边。然后,运行时引擎尝试保存部分未生成错误的记录。
  • 如果 Apex 中的 DML 语句生成了触发器,则任何错误都会回滚整个操作。但是,运行时引擎仍会处理操作中的每条记录以编译完整的错误列表。

触发器和调出

Apex 允许您调用 Apex 代码并将其与外部 Web 服务集成。对外部 Web 服务的 Apex 调用称之为调出。例如,您可以调出股票报价服务以获取最新报价。当从触发器发起调出时,必须异步执行,以便触发器进程不会在等待外部服务响应时阻止您的工作。异步调出在后台进程中进行,当外部服务返回结果时接收到响应。

要从触发器发起调出,请使用异步执行的类方法。这个类方法被称为 future 方法,并用 @future(callout=true) 注释。示例类中包含发起调出的 future 方法。

备注

本示例出于演示的目的,使用了虚构的端点 URL。除非您将端点更改为有效的 URL,并在 Salesforce 中为您的端点添加远程站点,否则您无法运行此示例。

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 发起调出

资源

在 Salesforce 帮助中分享 Trailhead 反馈

我们很想听听您使用 Trailhead 的经验——您现在可以随时从 Salesforce 帮助网站访问新的反馈表单。

了解更多 继续分享反馈