精通 Salesforce 自定义对象:面向开发人员的 Apex 与 SOQL 指南

背景与应用场景

作为一名 Salesforce 开发人员,我们的日常工作远不止处理标准的 Account、Contact 或 Opportunity 对象。Salesforce 平台最强大的功能之一就是其高度的可扩展性,而这种扩展性的核心便是 Custom Objects (自定义对象)。标准对象 (Standard Objects) 构成了 CRM 的通用基础,但每个企业的业务模式都是独一无二的。当标准对象无法满足特定的业务数据存储需求时,自定义对象就成为了我们构建定制化解决方案的基石。

想象一下以下场景:

  • 教育机构:需要一个“学生”对象 (Student__c) 来存储学号、专业、入学日期等信息,并与“课程”对象 (Course__c) 建立关联。
  • 房地产公司:需要一个“房产”对象 (Property__c) 来管理楼盘信息,包括地址、面积、价格、户型图等,并关联到潜在的“买家” (Contacts)。
  • 医疗系统:需要“病人”对象 (Patient__c) 和“病历”对象 (Medical_Record__c) 来跟踪患者的健康状况和就诊历史。

在这些场景中,强行使用标准对象会导致数据模型混乱、难以维护。自定义对象则允许我们为这些独特的业务实体创建专门的数据表,并定义它们自己的字段、关系、页面布局和业务逻辑。对于开发人员而言,自定义对象不仅是数据的容器,更是我们通过 ApexSOQLLightning Web Components 等技术进行交互、实现复杂业务流程的核心实体。


原理说明

从开发人员的视角来看,理解自定义对象的底层原理至关重要。这不仅仅是在设置菜单里点击几下那么简单,它关乎到我们在代码中如何高效、准确地与这些数据结构进行交互。

API 名称 (API Name)

每个自定义对象和自定义字段都有一个标签 (Label) 和一个 API 名称 (API Name)。标签是用户在界面上看到的名称,可以随时更改。而 API 名称则是我们在代码中引用的唯一标识符。自定义对象和字段的 API 名称总是以 `__c` 结尾(c 代表 custom)。例如,一个标签为“书籍”的自定义对象,其 API 名称可能是 `Book__c`。在编写 SOQL 查询或 Apex 代码时,我们必须严格使用 API 名称。

例如:`SELECT Name, Author__c FROM Book__c WHERE ISBN__c = '978-7-111-54413-7'`

这里的 `Book__c`, `Author__c`, 和 `ISBN__c` 都是 API 名称。

关系 (Relationships)

自定义对象可以通过关系字段与其他对象(标准或自定义)连接起来,形成复杂的数据模型。作为开发人员,我们主要关注两种关系:

  1. 查找关系 (Lookup Relationship):这是一种较为松散的父子关系。子对象上的查找字段指向父对象。在代码中,我们可以通过关系名称(通常是字段 API 名称去掉 `__c` 并加上 `__r`)来访问父对象的字段。例如,如果 `Book__c` 上有一个指向 `Publisher__c` 的查找字段 `Publisher__c`,我们可以这样查询:
    `SELECT Name, Publisher__r.Name FROM Book__c`
  2. 主从关系 (Master-Detail Relationship):这是一种更紧密的父子关系。子(Detail)记录的生命周期、所有权和共享权限都由父(Master)记录控制。如果删除了主记录,所有从属记录也会被级联删除。这种关系在数据模型上提供了更强的完整性保证。

在 Apex 中的表示

在 Apex 中,每个 Salesforce 对象(无论是标准还是自定义)都对应一个 SObject 类型。我们可以像实例化一个普通的类一样来创建自定义对象的实例。例如,要创建一个 `Book__c` 记录,我们可以这样做:

Book__c newBook = new Book__c();
newBook.Name = '深入浅出 Salesforce';
newBook.Author__c = '某位专家';

这种强类型的编程方式使得代码更易读、更安全,因为编译器可以在编译时就捕获到字段名拼写错误等问题。


示例代码

以下代码示例展示了作为开发人员如何通过 Apex 对一个名为 `Book__c` 的自定义对象(包含 `Name`, `Author__c`, `Published_Date__c` 字段)执行基本的 CRUD (Create, Read, Update, Delete) 操作。这些示例严格遵循 Salesforce 官方文档中的 DML (Data Manipulation Language) 和 SOQL (Salesforce Object Query Language) 语法。

1. 创建 (Create) 自定义对象记录

以下代码创建了一个 `Book__c` 对象的新实例,并将其插入到数据库中。

// DML Insert Example
// 官方文档参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_dml_insert.htm

// 1. 实例化一个自定义 SObject
Book__c newBook = new Book__c(
    Name = 'Apex 设计模式',
    Author__c = 'Salesforce大学',
    Published_Date__c = Date.newInstance(2023, 10, 1)
);

try {
    // 2. 使用 DML insert 语句将记录插入数据库
    insert newBook;
    
    // 3. 插入成功后,记录的 Id 字段会被自动填充
    System.debug('成功创建书籍记录,ID为: ' + newBook.Id);

} catch (DmlException e) {
    // 4. 捕获可能发生的 DML 异常,例如必填字段缺失
    System.debug('创建书籍记录失败: ' + e.getMessage());
}

2. 查询 (Read) 自定义对象记录

使用 SOQL 查询特定条件的 `Book__c` 记录。

// SOQL Query Example
// 官方文档参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_soql_select.htm

// 1. 创建一个 List 来存储查询结果,这是一个最佳实践
List<Book__c> books = [SELECT Id, Name, Author__c, Published_Date__c 
                     FROM Book__c 
                     WHERE Author__c = 'Salesforce大学' 
                     ORDER BY Published_Date__c DESC
                     LIMIT 10];

// 2. 遍历查询结果并处理
if (!books.isEmpty()) {
    for (Book__c book : books) {
        System.debug('书名: ' + book.Name + ', 作者: ' + book.Author__c);
    }
} else {
    System.debug('未找到符合条件的书籍。');
}

3. 更新 (Update) 自定义对象记录

先查询出需要更新的记录,修改其字段值,然后执行 DML update 操作。

// DML Update Example
// 官方文档参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_dml_update.htm

// 1. 首先通过 SOQL 查询需要更新的记录
List<Book__c> booksToUpdate = [SELECT Id, Name FROM Book__c WHERE Name = 'Apex 设计模式' LIMIT 1];

if (!booksToUpdate.isEmpty()) {
    // 2. 获取第一条记录并修改字段值
    Book__c bookToUpdate = booksToUpdate[0];
    bookToUpdate.Name = '高级 Apex 设计模式'; // 修改书名

    try {
        // 3. 执行 DML update 操作
        update bookToUpdate;
        System.debug('书籍记录更新成功。');
    } catch (DmlException e) {
        System.debug('更新书籍记录失败: ' + e.getMessage());
    }
}

4. 删除 (Delete) 自定义对象记录

查询到需要删除的记录,然后执行 DML delete 操作。

// DML Delete Example
// 官方文档参考: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_dml_delete.htm

// 1. 查询要删除的记录
List<Book__c> booksToDelete = [SELECT Id FROM Book__c WHERE Name LIKE '%测试%'];

if (!booksToDelete.isEmpty()) {
    try {
        // 2. 执行 DML delete 操作
        delete booksToDelete;
        System.debug('成功删除 ' + booksToDelete.size() + ' 条测试书籍记录。');
    } catch (DmlException e) {
        System.debug('删除书籍记录失败: ' + e.getMessage());
    }
}

注意事项

在以编程方式处理自定义对象时,必须牢记 Salesforce 平台的多租户架构和限制。

权限与安全性

Apex 代码默认在系统模式 (System Mode) 下运行,会忽略对象级权限 (Object-Level Security) 和字段级安全性 (Field-Level Security, FLS)。但是,为了构建安全的应用程序,我们必须主动检查用户的权限。可以使用 `with sharing` 或 `inherited sharing` 关键字来强制执行记录级别的共享规则。对于 FLS,应在执行 DML 或 SOQL 之前进行显式检查。

代码示例 - 检查字段可访问性:

// 在查询前检查用户是否有权访问 Author__c 字段
if (Schema.sObjectType.Book__c.fields.Author__c.isAccessible()) {
    List<Book__c> books = [SELECT Author__c FROM Book__c LIMIT 1];
    // ... process results
} else {
    System.debug('当前用户没有权限访问 Author__c 字段。');
    // 优雅地处理错误,而不是抛出异常
}

API 限制与批量化 (Bulkification)

Salesforce 强制执行严格的执行限制 (Governor Limits),以确保资源公平分配。例如,单个事务中 SOQL 查询不能超过 100 次,DML 操作不能超过 150 次。因此,严禁在循环中执行 SOQL 查询或 DML 操作。正确的做法是使用集合(如 List 或 Map)来批量处理数据。

错误示例 (不要这样做!):

List<Id> bookIds = getBookIds();
for (Id bookId : bookIds) {
    Book__c b = [SELECT Name FROM Book__c WHERE Id = :bookId]; // 在循环中执行 SOQL
    b.Name = b.Name + ' (V2)';
    update b; // 在循环中执行 DML
}

正确示例 (批量化):

List<Id> bookIds = getBookIds();
List<Book__c> booksToUpdate = [SELECT Name FROM Book__c WHERE Id IN :bookIds]; // 一次 SOQL 查询

for (Book__c b : booksToUpdate) {
    b.Name = b.Name + ' (V2)';
}

update booksToUpdate; // 一次 DML 操作

错误处理

DML 操作可能会因为验证规则、触发器逻辑或数据库限制而失败。除了使用 `try-catch` 块捕获异常外,还可以使用 `Database` 类的方法,如 `Database.insert(records, allOrNone)`。当 `allOrNone` 设置为 `false` 时,即使部分记录失败,成功的记录也会被提交。返回的 `Database.SaveResult` 对象数组可以用来详细检查每条记录的成功或失败状态。


总结与最佳实践

自定义对象是 Salesforce 平台的核心,也是我们开发人员构建强大、可扩展应用程序的画布。它们提供了超越标准 CRM 功能的无限可能性,让我们能够精确地映射和自动化任何业务流程。

作为开发人员,与自定义对象交互时,应遵循以下最佳实践:

  1. 始终使用 API 名称:在所有代码(Apex, SOQL, LWC JavaScript)中,坚持使用以 `__c` 结尾的 API 名称,而不是易变的标签。
  2. 代码必须批量化:永远不要在循环中放置 DML 或 SOQL。始终使用集合来处理批量数据,以避免触及 Governor Limits。
  3. 尊重安全模型:虽然 Apex 默认在系统模式下运行,但优秀的应用需要考虑用户权限。使用 `with sharing` 和 `Schema` 方法来编写安全的代码。
  4. 健壮的错误处理:预见并处理 DML 失败的可能。根据业务需求,选择 `try-catch` 或 `Database` 类方法来提供更优雅的用户体验。
  5. 清晰的命名约定:为自定义对象和字段提供清晰、一致的 API 名称和描述。这不仅有助于自己,也有助于未来维护代码的任何其他开发人员。

通过深入理解自定义对象的原理,并遵循这些开发最佳实践,我们可以构建出高效、稳定且可维护的 Salesforce 解决方案,真正释放平台为客户业务带来的价值。

评论

此博客中的热门博文

Salesforce 登录取证:深入解析用户访问监控与安全

Salesforce Experience Cloud 技术深度解析:构建社区站点 (Community Sites)

精通 Salesforce Email Studio:咨询顾问指南之 AMPscript 与数据扩展实现动态个性化邮件