Skip to main content
Build the future with Agentforce at TDX in San Francisco or on Salesforce+ on March 5–6. Register now.

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!');
    }
  4. Ctrl+S 进行保存。
  5. 要测试触发器,请创建一个客户。
    1. 单击 Debug(调试) | Execute Anonymous Window(打开执行匿名窗口)
    2. 在新窗口中,添加以下内容,然后单击 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 代码的维护。还允许您使用面向对象的编程。

以下示例展示了如何从触发器调用静态方法。如果触发器因插入事件而被触发,该示例将调用 EmailManager 类上的静态 sendMail() 方法。该实用程序方法向指定收件人发送电子邮件,并包含插入的联系人记录数。

  1. 在 Developer Console 中,单击 File(文件)| New(新建)| Apex Class(Apex 类)
  2. 输入 EmailManager,然后单击 OK(确定)。
  3. 用下面的 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;
        }
    }
  4. 在 Developer Console 中,单击 File(文件) | New(新建) | Apex Trigger(Apex 触发器)
  5. 输入 ExampleTrigger 作为触发器名称,然后为 sObject 选择 Contact(联系人)。单击 Submit(提交)
  6. 将默认代码替换为以下内容,然后将 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
        }
    }
  7. Ctrl+S 进行保存。
  8. 要测试触发器,请创建一个联系人。
    1. 单击 Debug(调试) | Execute Anonymous Window(打开执行匿名窗口)
    2. 在新窗口中,添加以下内容,然后单击 Execute(执行)
      Contact c = new Contact(LastName='Test Contact');
      insert c;
  1. 在调试日志中,检查触发器是否被触发。在日志末尾,找到由实用程序方法写入的调试消息:DEBUG|Email sent successfully
  2. 现在检查您是否收到一封电子邮件,正文文本内容为 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;
        }
    }
  2. 要测试该触发器,请在 Salesforce 用户界面中创建一个客户并将其命名为 Apples & Oranges
  3. 在客户页面的业务机会相关列表中,找到新创建的业务机会。触发器自动添加了该业务机会!
备注

您添加的触发器遍历触发器上下文的所有记录 — 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.');
        }
    }
  2. 在 Salesforce 用户界面,导航至 Apples & Oranges 客户页面,然后单击 Delete(删除)
  3. 在确认弹出窗口中,单击 OK(确定)。找到显示自定义错误提示消息

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

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

除非批量 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 帮助网站访问新的反馈表单。

了解更多 继续分享反馈