运行 Apex:匿名块、触发器与执行顺序完全指南
Apex运行机制完全指南:涵盖匿名块(权限要求/编译执行/local scope/virtual默认/禁止static)、触发器完整语法与事件类型(before vs after/upsert&merge行为)、全部上下文变量(Trigger.new/newMap/old/oldMap/isBefore/isAfter/isInsert/isUpdate/isDelete/isUndelete/operationType/size)与使用约束、批量触发器惯用模式(Set收集ID/Map组织查询结果/SOQL IN绑定)、完整20步服务器端执行顺序(Flow→before trigger→验证→重复规则→保存→after trigger→分配规则→工作流→汇总摘要→提交→提交后逻辑)、不触发触发器的操作列表以及Chatter/Knowledge文章触发器的特殊规则。
本课程共有 8 个章节
同学们,我们来看这一页幻灯片,它其实给咱们勾勒出了Apex这门语言能干哪些事儿,以及接下来咱们要重点学什么。 首先,Apex可以直接用代码去操控Salesforce界面上那些你平常点点点才能完成的功能,比如点击按钮、填写字段,这些操作背后的逻辑,你都能用Apex程序化实现。更厉害的是,它还能跟外部的Web服务打交道,不管是那种老式的SOAP接口,还是现在流行的REST接口,Apex都能轻松地跟它们集成,让Salesforce和外面的系统对话。 而且啊,Apex代码有一个非常重要的特性,就是它运行在“原子事务”里。啥意思呢?就是一段代码里可能做好几步操作,比如先更新一个客户,再给客户创建个订单,再记个日志。在Apex中,这些步骤要么全部成功,要么全部回滚,绝不会出现一半成功一半失败的情况。这就保证了数据的完整性和一致性,非常可靠。 那这一章呢,就是要带大家深入掌握怎么去“调用”Apex代码。我们会从最灵活、适合调试的“匿名块”讲起,然后重点学习触发器。触发器就是在记录被插入、更新、删除、取消删除,甚至是合并的时候,可以在操作之前或之后自动执行的Apex代码。我们会详细讲解触发器的语法结构,还有那些超级实用的上下文变量,比如你知道触发器里能区分当前操作是插入还是更新吗?它们都能通过变量拿到。 然后,我们会梳理一个完整的执行顺序:当一个记录被保存时,到底先跑系统验证,再跑前置触发器,再跑验证规则,再跑后置触发器……等等,这个顺序弄清了,你才能写出没有bug的逻辑。另外,我们必须得养成“批量触发器”的编程习惯,因为Salesforce可能一次给你一堆记录处理,不能只想着一条记录。最后,咱们还会总结一套触发器开发的最佳实践,帮你写出高效、好维护的代码。 好,这一页就是个路线图,接下来咱们就一块儿把这些知识点慢慢吃透。
我们开始今天的课程,来聊一聊 Salesforce Apex 里一个非常实用的小工具——,匿名块,。 你可以把它想象成一张“便签纸”,你临时想到了几行代码想要试试看,但又不想正儿八经地去创建一个类保存起来。这时候,匿名块就派上用场了。 它的几个最重要的特点你一定要记住: ,第一,它不会保存到系统里。, 你写的匿名块代码,只存在于你运行它的那一刻,跑完就消失了。它不是元数据,不会像类或触发器那样永久保留在组织中。所以它非常适合做临时测试、数据修补或者快速验证某个想法。 ,第二,每次运行都会重新编译。, 也就是说,你运行一次,Salesforce 就会现场把你的代码和任何引用到的类、对象编译一遍。这当然很方便,你可以随时修改随时跑。但如果同一个逻辑要被反复调用很多次,那性能就不太好了。比如你要批处理数据,还不停地用匿名块循环调用,那最好把它改成,已编译的 Apex 类,,或者暴露一个 ,Apex REST 端点,,这样只要编译一次,后面调用就很快了。 ,第三,匿名块里的方法不能是静态的。, 如果你在块里临时定义一个方法,前面是不允许加 `static` 关键字的。因为整个匿名块本身就类似于一个“静态上下文”,里面的方法限制为实例方法,但你可以直接运行块里的语句。 ,第四,事务控制很自动。, 如果你的匿名块里所有语句都执行成功,系统会自动提交数据库更改。但如果中间有任何未处理的异常导致运行失败,整个块的所有更改都会自动回滚,不会留下脏数据。这是一种安全的机制。 ,第五,它是在当前用户的权限下运行的。, 也就是说,你登录的这个用户对某些字段或对象如果没有读取或编辑权限,匿名块里要是去操作这些字段,可能连编译都过不了,直接报错。它不会绕过你的安全设置,这点要小心。 ,最后一点,关于内部定义的类和接口。, 在匿名块里,你其实也可以定义类或接口,方便做一些复杂逻辑。但这些类和接口默认就是 ,virtual, 的,即使你没有写 `virtual` 修饰符。而且它们同样,不会保存到组织中,,只在本次执行中有效。你可以把它们想象成用完即丢的临时模具。 所以总结一下:匿名块就是一块不保存、每次编译、权限跟随用户、自动提交回滚的“体验沙盒”,特别适合你随手试代码,但不适合做重复调用的生产逻辑。 好了,今天就讲到这里,你是不是对匿名块更清楚了呢?我们下节课见。
同学们,今天我们来看一下Apex里一个非常重要的概念——触发器。你可以把它理解成一种自动执行的代码,它能响应记录的变化,比如插入、更新、删除、合并、upsert甚至是取消删除这些操作。而且,它可以选择在操作发生之前或者之后运行。 我们先说before触发器,它在记录保存之前执行。最常见的作用就是提前更新字段的值,或者做一些数据校验。比如,你可以在保存之前检查一下金额是不是负数,如果不是,就自动填个默认值。 再来看after触发器,它在记录保存之后执行。这时候记录已经生成了Id和最后修改时间这些系统字段,所以你可以拿到它们来给别的记录做关联。但需要注意一点:在after触发器里,Trigger.new这个上下文变量是只读的,你不能再去修改当前这条记录的字段了,不过你可以用它来更新其他对象。 还有几个规则要记住:触发器代码里不能用static关键字;所有的数据库变更都是事务性的——操作成功就自动提交,中间一旦出错就整体回滚,很安全。最后,如果你用了upsert操作,系统会根据记录是否已存在,智能地触发对应的插入或更新触发器,不会重复执行。 好,关于触发器的基本要点就这些,大家可以先消化一下。
同学们,今天咱们来看看 Salesforce 里触发器自带的那些“隐式变量”。这些变量都是系统在 `System.Trigger` 这个类里帮咱们定义好的,写触发器时可以直接拿来用,不用自己声明。 先说说几个布尔类型的“上下文变量”,它们就像开关一样,能告诉你当前这个触发器正处在什么状态。比如 `isExecuting`,只要触发器代码正在跑,它永远都是 `true`。其他的呢:`isInsert` 表示现在是插入记录,`isUpdate` 是更新,`isDelete` 是删除。`isBefore` 和 `isAfter` 能区分是在保存前还是保存后触发。还有一个 `isUndelete`,代表是从回收站恢复记录。有了这些,你就能在代码里精确地判断当前到底是什么操作、哪个时机,再做出相应的处理。 接下来看记录集合。最常用的就是 `Trigger.new`,里面装着当前这一批要处理的新记录。注意哦,你只能在“保存前”的触发器里修改 `Trigger.new` 里的字段值,一旦到了“保存后”,它就变成只读的了。而 `Trigger.old`,不管在什么时候,都是只读的,它存的是这批记录修改前、或者被删除前的旧数据。如果处理的是列表形式的记录,还有 `Trigger.newMap` 和 `Trigger.oldMap`,它们是把记录映射成了 ID 到记录的格式,方便你通过记录 ID 快速找到记录。同样,`oldMap` 也只读。 还有一个 `Trigger.operationType`,它会返回一个枚举值,比如 `TriggerOperation.INSERT`、`TriggerOperation.UPDATE` 等等。这个特别适合在 `switch` 语句里使用,可以让你的代码更清晰地把不同操作类型的逻辑分开。 再说一个 `Trigger.size`,它返回的是当前触发器正在处理的记录批次里一共有多少条记录,这个在做限制检查的时候很实用。 最后提一个实用小技巧:`Trigger.new` 可以直接用作 SOQL 查询里 `IN` 子句的绑定变量。因为 `Trigger.new` 是 `List<SObject>` 类型,所以你可以写出像 `[SELECT Id, Name FROM Account WHERE Id IN :Trigger.new]` 这样的查询,不用自己先建一个 ID 列表,很方便。 好啦,这一页的内容就这些,都理解了的话,以后写触发器就能更得心应手了。
同学们,我们继续往下看。这一页 Slide 讲的是写触发器的时候,一个非常经典、也非常重要的批量处理模式。你学会了这个,写出来的代码才会又稳定又高效。 先看第一句:Trigger.new 和 trigger.old 不能用于 TLR 操作。这里的 TLR 呢,其实就是 Trigger Loop Recursion 的缩写,意思是“触发器的递归循环”。简单说,就是你别傻乎乎地直接拿 trigger.new 里面的记录,反反复复去更新同一批对象,那样很容易写出死循环,把你的整个事务搞崩掉。所以我们要换个思路,用更安全的手法来处理这些上下文数据。 那正确的手法是什么呢?就是在触发器里,大量使用 Set 和 Map。 首先,用 Set 来“
同学们,我们现在来聊聊在Salesforce里保存一条记录时,后台到底发生了哪些事情。这就像一条精心设计的流水线,每一步都有它的道理。理解这个顺序,对你写好触发器和自动化特别关键。 我们想象一下,当你点下保存按钮,Salesforce并不是直接就把数据丢进数据库,而是先做一系列准备和检查。首先,系统会把数据加载好,接着在第2步,就会跑第一遍验证规则,比如检查必填项、字段格式对不对,确保数据基础没问题。 然后到了第4步,很关键,,“之前触发”,就在这里执行。这个Apex before trigger让你有机会在数据真正存入数据库之前,再修改、再润色一下字段值。改完之后,不能直接放行,第5步会立即再次运行验证,确保你做的修改也是合法合规的。 验证通过,这时候数据才真正写入数据库,但事务还没有提交,还留着反悔的余地。紧接着第8步,,“之后触发”,执行,也就是after trigger。因为记录已经落库,此时你可以拿到记录ID,也能查询到数据库里的最新状态,适合做一些关联更新或日志记录,但记住,不能再直接修改当前记录本身了。 走出触发器,就轮到工作流规则登场。在原本的执行顺序里,工作流大概是第11步。这里有一个很重要的行为:如果工作流规则里包含了,字段更新,,它会像一个回声一样,引发一次额外的保存循环。也就是说,整个保存序列,包括前面的验证、before trigger、after trigger,都会被重新触发一遍。Salesforce限制这个回圈只会额外跑一次,避免无限循环,这个问题你要小心设计,别让触发器和工作流互相踢皮球。 再往后,流程自动化(比如Flow和Process Builder)在第13步左右运行。但是请注意,这里没有一个固定的执行顺序,你不能依赖哪个流程先跑。如果你的业务里有严格的顺序要求,比如必须先算折扣再算总价,那么最好改用,记录触发的流程,,在里面设定清晰的排序逻辑,这样才可控。 还有一种特殊情形,叫循环保存。在那种由于字段更新引发的二次保存过程中,步骤
同学们,今天我们来讲几个关于Apex触发器的关键注意事项,这些点能帮你写出更健壮、更高效的代码。 首先记住,当记录被合并时,触发器并不是按我们直觉以为的那样触发合并事件,而是会激发删除和更新触发器。也就是说,系统会先删除被合并掉的旧记录,然后更新保留下来的主记录。所以你的删除和更新触发器必须能正确处理合并引发的操作。 接下来是“after undelete”触发器。它比较特殊,只会在顶级对象上触发。比如你直接从回收站恢复账户,触发器会运行;但如果是因为恢复了一个关联了该账户的子记录而级联恢复,它是不会触发的。这点在做审计或依赖恢复逻辑时很容易踩坑。 另外,很多系统级的批量操作,比如数据导入工具或者某些后台大容量处理,默认是不调用触发器的。如果你的业务逻辑高度依赖触发器,就需要在实施前确认使用的工具是否支持触发,或者设计备用的验证机制。 说到错误处理,我们经常用 `addError()` 来阻止不合规的DML操作,并且给出友好的自定义消息。和用验证规则相比,触发器里的 `addError()` 响应更快,而且逻辑可以更复杂,是首选的拦截手段。 最关键的设计原则是:,永远为批量处理设计触发器,。绝对不要假设触发器一次只处理一条记录。比如你写 `trigger.old[0]`,当批量更新200条记录时就会出错。应该遍历 `Trigger.old` 或 `Trigger.new` 列表。 在写SOQL查询时,尽量用集合(Set)和 `IN` 子句,把所有需要的ID收集起来,一次性查询,避免循环里出现SOQL。同理,DML操作也要收集到列表里,最后统一执行,而不是一条条 update。这样做能大幅减少DML语句的执行次数,显著提高性能。 把这些要点融入你的编码习惯,会让你的触发器又快又稳,适应各种真实场景。好,关于触发器的这些核心规则,大家理解了吗?我们下节继续深入。
同学们,我们这节课来看一下,在使用 Chatter 触发器时需要注意的几个关键点。 首先,关于 Chatter 的插入触发器,它并不是对所有动态类型都生效的。只有特定的 FeedItem 类型才会触发插入触发器,比如文本帖子、链接帖子这些常见的类型。而有些通过系统自动生成的动态,可能就不会激活你的触发器代码,这一点在做开发时要特别留意。 接下来是附件的问题。Chatter 中的附件记录——也就是 FeedAttachment,是不能直接通过 Apex 更新的,我们只能通过 SOQL 去查询它。而且,如果你想把一个文件关联到一条动态上,是有顺序的:一定要先成功插入 FeedItem 记录,然后再去创建对应的 FeedAttachment 记录,否则会出错。 还有一点很容易被忽略,就是知识文章的发布和存档操作,并不会触发 Chatter 触发器。也就是说,即便你为 FeedItem 写了很完善的触发逻辑,当一篇文章被发布时,系统不会去执行这些代码。所以千万不要依赖触发器来处理知识文章相关的 Chatter 更新。 最后,我们再来看一类常见的字段修改限制。有一些对象上的字段,是没办法在 before 触发器中随意修改的,因为它们的值是由系统