Trigger.new vs Trigger.old — 正确理解 Apex 触发器
Source: Dev.to
(请提供需要翻译的正文内容,我才能为您完成简体中文的翻译。)
介绍
在学习 Apex 触发器时,关键是要弄清何时使用 Trigger.new 与 Trigger.old。
- Trigger.new – Salesforce 正在尝试保存的当前数据。
- Trigger.old – 变更之前已经存在的数据。
牢记这一区别,大多数使用场景就会变得合乎逻辑。
Trigger.new
它代表什么
Trigger.new 包含 Salesforce 正在尝试插入或更新的当前或即将到来的值。
常见用例
- 验证
- 设置或修改字段值
- 读取已更新的值
- 基于新数据创建关联记录
在以下上下文中可用
| 场景 | 是否可用 |
|---|---|
| Insert(插入) | ✅ |
| Update(更新) | ✅ |
| Undelete(恢复) | ✅ |
Undelete:当记录从回收站恢复时会触发此上下文,Trigger.new 包含被恢复的记录值。
示例:验证
for (Contact con : Trigger.new) {
if (con.Email == null) {
con.Email.addError('Email is required');
}
}
该验证能够工作是因为 Trigger.new 代表了当时正在保存的数据。
Trigger.old
它代表什么
Trigger.old 是记录在更改 之前 的快照。
常见用例
- 检测字段变化
- 基于先前值的条件逻辑
- 防止重复操作
可用的上下文
| 场景 | 是否可用 |
|---|---|
| Update | ✅ |
| Delete | ✅ |
示例:检测变化
for (Opportunity newOpp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(newOpp.Id);
if (newOpp.StageName != oldOpp.StageName) {
// Stage has changed
}
}
Trigger.old 提供用于比较的先前状态。
示例:检测有意义的更新
for (Opportunity newOpp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(newOpp.Id);
if (newOpp.StageName == 'Closed Won' &&
oldOpp.StageName != 'Closed Won') {
// Stage just changed to Closed Won
}
}
提示
- Trigger.oldMap 和 Trigger.newMap 可快速通过 Id 访问记录(
Id → Record)。 - 在批量操作中比较旧值和新值时,使用映射比循环更佳。
- 只要你的逻辑依赖于检测变化(而不仅是当前值),就需要同时使用
Trigger.new和Trigger.old。
Source: …
基于上下文的决策表
| 需求 | 用途 |
|---|---|
| 验证输入 | Trigger.new |
| 修改字段 | Trigger.new |
| 比较新旧记录 | Trigger.new + Trigger.old |
| 删除前清理 | Trigger.old |
| 阻止记录保存 | Trigger.new.addError() |
触发器类型
| 触发器类型 | 操作说明 |
|---|---|
| Before | 修改 Trigger.new |
| After | 读取 Trigger.new,并与 Trigger.old 比较 |
重要: 在 after 触发器中不能使用
Trigger.new修改记录,因为记录已经提交到数据库。此时Trigger.new为只读。任何进一步的更改都需要单独的 DML 操作。
示例:在 after 触发器中使用 DML 更新
List<Account> accsToUpdate = new List<Account>();
for (Account acc : Trigger.new) {
accsToUpdate.add(new Account(
Id = acc.Id,
Name = 'Updated Name'
));
}
update accsToUpdate;
触发器上下文概览
| 上下文 | 可用数据 | 目的 |
|---|---|---|
| 插入前 | Trigger.new | 记录尚不存在;在保存前修改字段 |
| 更新前 | Trigger.new + Trigger.old | 对比旧值和新值;在保存前修改字段 |
| 更新后 | Trigger.new + Trigger.old | 记录已保存;只读,检测变化 |
| 删除前 | Trigger.old | 记录即将被删除;执行清理逻辑 |
| 删除后 | Trigger.old | 记录已删除;只读,用于审计或相关逻辑 |
可视化数据的时间线可以自然地帮助选择正确的上下文。与其记忆规则,不如问自己:
“我是在处理 new 数据、old 数据,还是它们之间的 变化?”
一旦回答明确,合适的上下文(Trigger.new、Trigger.old 或两者)就显而易见。
最佳实践
- Bulkify 你的触发器 – 循环遍历集合,如
Trigger.new,而不是单条记录,以避免治理限制。 - 使用
Trigger.newMap和Trigger.oldMap通过 Id 进行高效查找,而不是使用嵌套循环。