Apex 触发器:从入门到批量设计模式

Apex 触发器让你在记录插入、更新、删除前后执行自定义逻辑。本文从触发器语法、上下文变量、调用类方法、addError 限制保存,讲到 callout 异步处理,再到批量设计模式:操作记录集合、批量 SOQL 和批量 DML,帮你写出高性能、不超限的触发器。...

📅 2026/9/25 ✍️ ponybai 🏷️ salesforce, apex, developer, headless

学习目标

slide_2

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

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

编写 Apex 触发器

slide_3

Apex 触发器让你能在 Salesforce 中记录的插入、更新、删除等事件之前或之后执行自定义操作。就像数据库系统支持触发器一样,Apex 也为管理记录提供了触发器支持。

通常用触发器来根据特定条件执行操作、修改相关记录,或限制某些操作。你可以用触发器做任何在 Apex 中能做的事,包括执行 SOQL、DML 或调用自定义 Apex 方法。

当验证字段值或更新记录字段时,应使用验证规则和 Flow;当性能和规模很重要、逻辑对点选工具来说太复杂,或需要执行 CPU 密集型操作时,才使用 Apex 触发器。触发器可为顶级标准对象、自定义对象和部分标准子对象定义,创建后默认处于激活状态。

触发器语法

slide_4

触发器定义的语法与类定义不同:以 trigger 关键字开头,后面依次是触发器名称、关联的 Salesforce 对象,以及触发条件。

要指定在插入、更新、删除、取消删除操作之前或之后执行,可以在逗号分隔的列表中指定多个触发事件,可用事件包括:

  • before insert、before update、before delete
  • after insert、after update、after delete、after undelete

触发器示例

slide_5

一个简单触发器在插入账户之前触发,并向调试日志写入一条消息。以 HelloWorldTrigger 为例:在 Developer Console 中新建 Account 上的 Apex 触发器,用 System.debug('Hello World!') 替换默认代码并保存。测试时创建账户,即可在调试日志中找到 Hello World! 语句,并确认触发器已被执行。

触发器的类型

slide_6

触发器有两种类型:

  • Before 触发器:在记录保存到数据库之前,用于更新或验证记录值。
  • After 触发器:用于访问系统设置的字段值(如记录的 Id 或 LastModifiedDate),以及影响其他记录的变更。触发 after 触发器的记录是只读的。

使用上下文变量

slide_7

要访问触发触发器的记录,使用上下文变量。例如 Trigger.new 包含插入或更新触发器中所有已插入的记录,Trigger.old 提供更新前记录的旧版本(或删除触发器中的已删除记录列表)。触发器既可能因单条记录触发,也可能因 API 或 Apex 批量插入多条记录触发,所以 Trigger.new 可能包含一条或多条记录,可用 for 循环遍历。

其他常用上下文变量包括:布尔型变量(如 isInsert、isUpdate、isDelete、isBefore、isAfter、isUndelete),Trigger.operationType(返回 System.TriggerOperation 枚举),以及 Trigger.size(当前批次处理的记录数,DML 超过 200 条时按批次处理)。注意:before 触发器中可修改 Trigger.new 的记录,无需显式调用 DML。

从触发器调用类方法

slide_8

可以从触发器中调用公共的实用方法。调用其他类的方法能实现代码复用、减小触发器体积,并改善 Apex 代码的可维护性,还能使用面向对象编程。

示例:当触发器因插入事件触发时,调用 CustomContactNotification 类的静态 notifyUsers() 方法,向 Salesforce 用户发送自定义通知(通知中包含插入的联系人记录数量)。这需要先在 Setup 中用 Notification Builder 创建一个自定义通知。

添加相关记录

slide_9

触发器常被用来访问和管理与触发器上下文中记录相关的记录。示例 AddRelatedRecord 触发器:为每个新建或更新的账户添加一个相关商机(如果该账户还没有商机)。触发器先执行 SOQL 查询获取所有账户的子商机,然后遍历 Trigger.new 中的账户,若账户没有相关商机则创建一条,最后统一插入。

这个触发器的 for 循环遍历了所有记录,但实际只需要「没有商机的账户」这个子集。下一单元将演示如何只获取没有商机的账户,从而让触发器更高效。

使用触发器异常

slide_10

有时需要对某些数据库操作添加限制,例如在满足特定条件时阻止记录保存。要在触发器中阻止保存记录,对相关 sObject 调用 addError() 方法——它会在触发器内抛出致命错误,错误消息显示在用户界面并记录日志。

示例 AccountDeletion 触发器:删除账户默认会级联删除其所有相关记录,该触发器在账户有关联商机时阻止删除,抛出错误「Cannot delete account with related opportunities.」。

在触发器中调用 addError() 会导致整组操作回滚,除非批量 DML 以部分成功方式调用:若由 Salesforce Platform API 的批量 DML 触发,运行时会把出错记录放到一边并尝试部分保存;若由 Apex 中的 DML 语句触发,任何错误都会回滚整个操作,但运行时仍会处理每条记录以汇总完整错误列表。

触发器与 Callout

slide_11

Apex 可以调用外部 Web 服务(称为 callout)。从触发器发起 callout 时,必须异步执行,这样触发器进程不会在等待外部服务响应时阻塞你的工作。异步 callout 在后台进程中执行,外部服务返回时再接收响应。

要从触发器发起异步 callout,使用 Queueable 接口并加上 Database.AllowsCallouts 标记。示例中 CalloutQueueable 类实现了 Queueable 接口,其 execute() 方法定义 callout;触发器调用 System.enqueueJob() 把 Queueable 实例加入异步作业队列。

批量 Apex 触发器

slide_12

本单元介绍批量触发器的设计模式,让触发器在大数据量下高效运行、不超出平台限制。

学习目标

slide_13

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

  • 编写操作 sObject 集合的触发器。
  • 编写执行高效 SOQL 和 DML 操作的触发器。

批量触发器设计模式

slide_14

Apex 触发器天生为批量操作而优化。推荐在触发器中使用批量设计模式:这样触发器性能更好、消耗更少的服务器资源,更不容易超出平台限制。批量化的代码能高效处理大量记录,并在 Salesforce Platform 的 governor limits(治理限制)内运行——这些限制用于确保失控代码不会独占多租户平台上的资源。

操作记录集合

slide_15

批量触发器操作触发器上下文中的所有 sObject。若触发操作来自用户界面,通常只操作一条记录;但若来自批量 DML 或 API,触发器操作的是一个记录集合。因此好的编程实践是:始终假设触发器操作的是一个记录集合,这样它在所有情况下都能正常工作。

反例 MyTriggerNotBulk 假设只有一条记录触发,多条记录在同一事务插入时无法处理整组;批量版本 MyTriggerBulk 用 for 循环遍历所有 sObject,无论 Trigger.new 含一条还是多条都能工作。

执行批量 SOQL

slide_16

SOQL 查询很强大,能在一个查询中检索相关记录并检查多个条件的组合,从而写更少代码、发更少查询。减少数据库查询有助于避免触及查询限制(同步 Apex 为 100 条 SOQL 查询,异步为 200 条)。

反例 SoqlTriggerNotBulk 在 for 循环内做 SOQL 查询——每个账户循环一次,账户很多时会超出查询限制。推荐的 SoqlTriggerBulk 把 SOQL 查询移到循环外,只执行一次:用内查询 (SELECT Id FROM Opportunities) 获取相关商机,并用 WHERE Id IN :Trigger.new 把查询关联到触发上下文的记录。也可以只查询相关商机(WHERE AccountId IN :Trigger.new),或用 SOQL for 循环合并查询与循环。注意触发器按每批 200 条记录执行,因此 SOQL for 循环在触发器中不会带来额外批量收益。

执行批量 DML

slide_17

在触发器或类中执行 DML 时,应尽可能对 sObject 集合执行 DML。对每个 sObject 单独执行 DML 会低效消耗资源——Apex 运行时一个事务最多允许 150 次 DML 调用。

反例 DmlTriggerNotBulk 在 for 循环内对每个商机调用一次 update;批量版本 DmlTriggerBulk 在循环中把要更新的商机加入列表 oppsToUpdate,循环结束后对该列表执行一次 DML——无论更新多少条记录,都只用一次 DML 调用。

批量设计模式实战:获取相关记录

slide_18

把学到的设计模式应用到 AddRelatedRecord 触发器上。它针对插入或更新的账户触发,为每个还没有商机的账户添加默认商机。批量版本用 switch 语句按 Trigger.operationType 区分插入和更新:插入时把新账户直接加入 toProcess 列表;更新时用 SOQL 查询找出没有相关商机的账户。最后 for 循环遍历 toProcess,添加默认商机到 oppList,再统一 insert。测试时创建账户(如「Lions and Cats」),即可在其商机相关列表中找到自动添加的商机。


文章来源:Trailhead - Apex Triggers