Salesforce的Trigger触发器怎么使用

发布时间:2022-03-25 16:27:06 作者:iii
来源:亿速云 阅读:244
# Salesforce的Trigger触发器怎么使用

## 目录
1. [Trigger基础概念](#1-trigger基础概念)
   - 1.1 [什么是Trigger](#11-什么是trigger)
   - 1.2 [Trigger执行上下文](#12-trigger执行上下文)
   - 1.3 [Trigger与工作流规则的区别](#13-trigger与工作流规则的区别)
2. [Trigger语法结构](#2-trigger语法结构)
   - 2.1 [基本语法格式](#21-基本语法格式)
   - 2.2 [Trigger事件类型](#22-trigger事件类型)
   - 2.3 [上下文变量](#23-上下文变量)
3. [Trigger最佳实践](#3-trigger最佳实践)
   - 3.1 [批量处理原则](#31-批量处理原则)
   - 3.2 [避免递归触发](#32-避免递归触发)
   - 3.3 [使用Handler模式](#33-使用handler模式)
4. [常见Trigger模式](#4-常见trigger模式)
   - 4.1 [字段自动填充](#41-字段自动填充)
   - 4.2 [数据验证](#42-数据验证)
   - 4.3 [关联记录操作](#43-关联记录操作)
5. [高级Trigger技巧](#5-高级trigger技巧)
   - 5.1 [静态变量控制递归](#51-静态变量控制递归)
   - 5.2 [异步Trigger处理](#52-异步trigger处理)
   - 5.3 [Trigger与Future方法结合](#53-trigger与future方法结合)
6. [调试与测试](#6-调试与测试)
   - 6.1 [调试日志分析](#61-调试日志分析)
   - 6.2 [测试类编写规范](#62-测试类编写规范)
   - 6.3 [测试覆盖率提升](#63-测试覆盖率提升)
7. [性能优化](#7-性能优化)
   - 7.1 [SOQL查询优化](#71-soql查询优化)
   - 7.2 [DML操作优化](#72-dml操作优化)
   - 7.3 [集合使用技巧](#73-集合使用技巧)
8. [常见问题与解决方案](#8-常见问题与解决方案)
   - 8.1 [Governor Limit处理](#81-governor-limit处理)
   - 8.2 [混合DML错误](#82-混合dml错误)
   - 8.3 [死锁问题](#83-死锁问题)
9. [实际案例解析](#9-实际案例解析)
   - 9.1 [客户评分自动计算](#91-客户评分自动计算)
   - 9.2 [订单状态同步](#92-订单状态同步)
   - 9.3 [跨对象数据集成](#93-跨对象数据集成)
10. [总结与资源](#10-总结与资源)

## 1. Trigger基础概念

### 1.1 什么是Trigger

Salesforce Trigger(触发器)是Apex代码的一种特殊类型,它在特定数据操作事件(如记录插入、更新、删除等)发生时自动执行。Trigger允许开发人员在数据变更前后执行自定义业务逻辑,是实现复杂业务流程的核心工具。

**关键特性:**
- 与特定sObject关联
- 响应DML操作自动触发
- 在事务上下文中执行
- 可以访问操作记录的旧值和新值

### 1.2 Trigger执行上下文

Trigger在特定的执行上下文中运行,理解这些上下文对编写高效Trigger至关重要:

| 上下文变量       | 描述                                                                 |
|------------------|----------------------------------------------------------------------|
| `Trigger.isBefore` | 在数据保存到数据库前执行                                             |
| `Trigger.isAfter`  | 在数据保存到数据库后执行                                             |
| `Trigger.new`      | 包含要插入或更新的记录新版本(仅适用于insert和update操作)           |
| `Trigger.old`      | 包含更新前或删除前的记录旧版本(仅适用于update和delete操作)         |
| `Trigger.size`     | 触发器中包含的记录总数                                               |

### 1.3 Trigger与工作流规则的区别

虽然Trigger和工作流规则都能实现自动化业务逻辑,但二者有本质区别:

| 特性                | Trigger                          | 工作流规则                   |
|---------------------|----------------------------------|----------------------------|
| 灵活性              | 完全编程控制                     | 配置化,功能有限           |
| 处理能力            | 支持复杂业务逻辑                 | 简单字段更新和通知         |
| 执行上下文          | Before/After均可                 | 仅After                    |
| 批量处理            | 原生支持                         | 单条记录处理               |
| 测试要求            | 需要75%以上测试覆盖率            | 无需测试                   |
| 调试复杂度          | 较高                             | 较低                       |

## 2. Trigger语法结构

### 2.1 基本语法格式

```apex
trigger TriggerName on ObjectName (trigger_events) {
    // Trigger逻辑代码
}

示例:

trigger AccountTrigger on Account (before insert, after update) {
    if(Trigger.isBefore && Trigger.isInsert) {
        // 插入前逻辑
    } else if(Trigger.isAfter && Trigger.isUpdate) {
        // 更新后逻辑
    }
}

2.2 Trigger事件类型

Salesforce支持7种Trigger事件:

  1. before insert - 记录插入前
  2. after insert - 记录插入后
  3. before update - 记录更新前
  4. after update - 记录更新后
  5. before delete - 记录删除前
  6. after delete - 记录删除后
  7. after undelete - 记录恢复后

2.3 上下文变量

完整上下文变量列表:

// 判断Trigger类型
Trigger.isExecuting
Trigger.isInsert
Trigger.isUpdate
Trigger.isDelete
Trigger.isUndelete
Trigger.isBefore
Trigger.isAfter

// 记录集合
Trigger.new
Trigger.newMap
Trigger.old
Trigger.oldMap

// 其他信息
Trigger.operationType
Trigger.size

3. Trigger最佳实践

3.1 批量处理原则

关键点: - 所有Trigger都应设计为能处理200条记录的批量操作 - 避免在循环内执行SOQL查询或DML操作 - 使用集合类(List, Set, Map)处理记录

不良实践:

trigger BadExample on Contact (before update) {
    for(Contact c : Trigger.new) {
        // 循环内执行SOQL - 违反批量原则
        Account a = [SELECT Id FROM Account WHERE Name = :c.Account_Name__c];
        c.AccountId = a.Id;
    }
}

优化后:

trigger GoodExample on Contact (before update) {
    Set<String> accountNames = new Set<String>();
    for(Contact c : Trigger.new) {
        accountNames.add(c.Account_Name__c);
    }
    
    Map<String, Account> accountMap = new Map<String, Account>();
    for(Account a : [SELECT Id, Name FROM Account WHERE Name IN :accountNames]) {
        accountMap.put(a.Name, a);
    }
    
    for(Contact c : Trigger.new) {
        if(accountMap.containsKey(c.Account_Name__c)) {
            c.AccountId = accountMap.get(c.Account_Name__c).Id;
        }
    }
}

3.2 避免递归触发

递归触发会导致Governor Limit问题和不可预期的行为:

解决方案: 1. 使用静态变量控制

public class TriggerControl {
    public static Boolean isFirstRun = true;
}

trigger AccountTrigger on Account (before update) {
    if(TriggerControl.isFirstRun) {
        TriggerControl.isFirstRun = false;
        // 业务逻辑
    }
}
  1. 设计业务逻辑避免自触发

3.3 使用Handler模式

将业务逻辑从Trigger转移到单独的Handler类:

// Trigger文件
trigger AccountTrigger on Account (before insert, before update) {
    AccountTriggerHandler.handleBeforeInsertUpdate(Trigger.new);
}

// Handler类
public class AccountTriggerHandler {
    public static void handleBeforeInsertUpdate(List<Account> accounts) {
        // 业务逻辑实现
    }
}

优势: - 代码更易维护 - 逻辑可复用 - 便于单元测试 - 减少Trigger复杂度

4. 常见Trigger模式

4.1 字段自动填充

trigger OpportunityTrigger on Opportunity (before insert, before update) {
    for(Opportunity opp : Trigger.new) {
        // 自动设置关闭日期为创建日期+30天
        if(opp.CloseDate == null) {
            opp.CloseDate = Date.today().addDays(30);
        }
        
        // 自动计算金额
        if(opp.Quantity__c != null && opp.UnitPrice__c != null) {
            opp.Amount = opp.Quantity__c * opp.UnitPrice__c;
        }
    }
}

4.2 数据验证

trigger ContactTrigger on Contact (before insert, before update) {
    for(Contact con : Trigger.new) {
        // 验证电子邮件格式
        if(con.Email != null && !Pattern.matches('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}', con.Email)) {
            con.Email.addError('请输入有效的电子邮件地址');
        }
        
        // 业务规则验证
        if(con.Department == 'Finance' && con.Title == null) {
            con.Title.addError('财务部门联系人必须指定职位');
        }
    }
}

4.3 关联记录操作

trigger CaseTrigger on Case (after insert) {
    if(Trigger.isAfter && Trigger.isInsert) {
        List<Task> tasks = new List<Task>();
        
        for(Case c : Trigger.new) {
            // 为每个新Case创建跟进任务
            tasks.add(new Task(
                WhatId = c.Id,
                Subject = '初始跟进',
                Priority = 'Normal',
                Status = 'Not Started',
                ActivityDate = Date.today().addDays(1)
            );
        }
        
        if(!tasks.isEmpty()) {
            insert tasks;
        }
    }
}

5. 高级Trigger技巧

5.1 静态变量控制递归

public class RecursionControl {
    private static Set<Id> processedAccountIds = new Set<Id>();
    
    public static Boolean hasAlreadyProcessed(Id accountId) {
        if(processedAccountIds.contains(accountId)) {
            return true;
        } else {
            processedAccountIds.add(accountId);
            return false;
        }
    }
}

trigger AccountTrigger on Account (after update) {
    for(Account acc : Trigger.new) {
        if(!RecursionControl.hasAlreadyProcessed(acc.Id)) {
            // 执行业务逻辑
        }
    }
}

5.2 异步Trigger处理

trigger OrderTrigger on Order (after update) {
    if(Trigger.isAfter && Trigger.isUpdate) {
        Set<Id> orderIds = new Set<Id>();
        for(Order o : Trigger.new) {
            if(o.Status == 'Activated' && Trigger.oldMap.get(o.Id).Status != 'Activated') {
                orderIds.add(o.Id);
            }
        }
        
        if(!orderIds.isEmpty()) {
            System.enqueueJob(new OrderProcessingQueueable(orderIds));
        }
    }
}

5.3 Trigger与Future方法结合

trigger ContactTrigger on Contact (after update) {
    if(Trigger.isAfter && Trigger.isUpdate) {
        Set<Id> accountIds = new Set<Id>();
        for(Contact con : Trigger.new) {
            if(con.AccountId != null && con.Email != Trigger.oldMap.get(con.Id).Email) {
                accountIds.add(con.AccountId);
            }
        }
        
        if(!accountIds.isEmpty()) {
            updateAccountContactsFuture(accountIds);
        }
    }
}

@future
public static void updateAccountContactsFuture(Set<Id> accountIds) {
    // 异步处理逻辑
}

6. 调试与测试

6.1 调试日志分析

使用System.debug()输出调试信息:

trigger DebugExample on Account (before update) {
    System.debug('Trigger.new size: ' + Trigger.new.size());
    
    for(Account acc : Trigger.new) {
        System.debug('Processing account: ' + acc.Id);
        System.debug('Old Name: ' + Trigger.oldMap.get(acc.Id).Name);
        System.debug('New Name: ' + acc.Name);
    }
}

查看日志: 1. 设置 → 监控 → 调试日志 2. 添加用户跟踪 3. 执行触发操作 4. 查看生成的调试日志

6.2 测试类编写规范

@isTest
private class AccountTriggerTest {
    
    @isTest
    static void testBeforeInsert() {
        // 准备测试数据
        Account testAcc = new Account(
            Name = 'Test Account',
            Industry = 'Technology'
        );
        
        Test.startTest();
        insert testAcc;
        Test.stopTest();
        
        // 验证结果
        Account insertedAcc = [SELECT CustomerPriority__c FROM Account WHERE Id = :testAcc.Id];
        System.assertEquals('High', insertedAcc.CustomerPriority__c, '优先级未正确设置');
    }
    
    @isTest
    static void testBulkInsert() {
        // 批量测试
        List<Account> accounts = new List<Account>();
        for(Integer i=0; i<200; i++) {
            accounts.add(new Account(
                Name = 'Test Account ' + i
            ));
        }
        
        Test.startTest();
        insert accounts;
        Test.stopTest();
        
        // 验证所有记录处理
        List<Account> insertedAccounts = [SELECT Id FROM Account WHERE Name LIKE 'Test Account %'];
        System.assertEquals(200, insertedAccounts.size());
    }
}

6.3 测试覆盖率提升

技巧: 1. 测试所有Trigger上下文 2. 测试正向和负向场景 3. 测试批量操作 4. 测试异常情况 5. 使用Test.loadData方法加载测试数据 6. 利用@testSetup方法

7. 性能优化

7.1 SOQL查询优化

最佳实践: - 将SOQL移出循环 - 使用WHERE IN子句 - 只查询需要的字段 - 利用索引字段(标准索引字段:Id, Name, 外部ID, 主从关系字段等)

// 差: 循环内查询
for(Contact c : Trigger.new) {
    Account a = [SELECT Id, Name FROM Account WHERE Id = :c.AccountId];
}

// 好: 批量查询
Set<Id> accountIds = new Set<Id>();
for(Contact c : Trigger.new) {
    accountIds.add(c.AccountId);
}
Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);

7.2 DML操作优化

关键点: - 批量执行DML操作 - 使用Database方法处理部分成功 - 考虑使用急切加载

// 差: 循环内插入
for(Contact c : contactsToCreate) {
    insert c;
}

// 好: 批量插入
insert contactsToCreate;

// 更好: 带错误处理的部分成功
Database.SaveResult[] srList = Database.insert(contactsToCreate, false);
for(Database.SaveResult sr : srList) {
    if(!sr.isSuccess()) {
        System.debug('插入失败: ' + sr.getErrors());
    }
}

7.3 集合使用技巧

高效集合操作:

// 使用Set去重
Set<Id> accountIds = new Set<Id>();
for(Opportunity opp : Trigger.new) {
    accountIds.add(opp.AccountId);
}

// 使用Map快速查找
Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);

// 使用List保持顺序
List<Account> sortedAccounts = new List<Account>();
sortedAccounts.addAll(accountMap.values());
sortedAccounts.sort();

8. 常见问题与解决方案

8.1 Governor Limit处理

常见限制及解决方案:

限制类型 限制值 解决方案
SOQL查询总数 100次 批量查询,使用Map缓存结果
DML语句总数 150次 批量DML操作
CPU时间 10,000毫秒 优化算法复杂度,考虑异步处理
堆大小 6MB/12MB 减少变量使用,分批次处理数据
查询返回记录数 50,000条 添加更精确的WHERE条件,使用LIMIT子句

8.2 混合DML错误

问题描述: 在同一个事务中处理设置对象(setup object)和非设置对象时会出现混合DML错误。

解决方案:

// 错误示例
trigger UserTrigger on User (after update) {
    // 同时更新用户和普通对象会报错
    update [SELECT Id FROM Account LIMIT 1];
}

// 正确方式:使用@future方法
trigger UserTrigger on User (after update) {
    System.enqueueJob(new MixedDMLHandler(Trigger.newMap.keySet()));
}

public class MixedDMLHandler implements Queueable {
    private Set<Id> userIds;
    
    public MixedDMLHandler(Set<Id> userIds) {
        this.userIds = userIds;
    }
    
    public void execute(QueueableContext context) {
        // 这里可以安全执行混合DML
        update [SELECT Id FROM Account LIMIT 1];
    }
}

8.3 死锁问题

预防措施: 1. 避免在Trigger中更新触发对象自身 2. 减少交叉对象更新 3. 使用异步处理分离事务 4. 优化SOQL查询条件减少锁定范围

9. 实际案例解析

9.1 客户评分自动

推荐阅读:
  1. mysql触发器(trigger)
  2. MySQL触发器trigger的使用

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

trigger salesforce

上一篇:Salesforce的Formula公式怎么创建

下一篇:Salesforce的验证规则是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》