Skip to main content

通过 DML 操作记录

学习目标

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

  • 使用 DML 插入、更新和删除记录。
  • 批量执行 DML 语句。
  • 使用 upsert 插入或更新记录。
  • 捕获 DML 异常。
  • 使用数据库方法插入带有部分成功选项的新记录并处理结果。
  • 清楚何时使用 DML 语句以及何时使用数据库方法。
  • 对相关记录执行 DML 操作。
备注

备注

用中文(简体)学习?在中文(简体)Trailhead Playground 中开始挑战,用括号中提供的译文完成挑战。仅复制并粘贴英文值,因为挑战验证基于英文数据。如果在中文(简体)组织中没有成功通过挑战,我们建议您 (1) 将区域设置切换为美国,(2) 按此处说明将语言切换为英文,(3) 再次单击“检查挑战”按钮。

查看 Trailhead 本地化语言徽章详细了解如何利用 Trailhead 译文。

通过 DML 操作记录

使用 Data Manipulation Language(简称 DML)在 Salesforce 中创建和修改记录。DML 提供简单语句用于插入、更新、合并、删除和恢复记录操作,方便您以简单直接的方式来管理记录。

Apex 是一种以数据为中心的语言,并且保存在 Lightning 平台上,便于您直接访问 Salesforce 中的数据。不同于需要额外设置才能连接到数据源的其他编程语言,使用 Apex DML 可以轻松管理记录!通过调用 DML 语句,您可以快速地对 Salesforce 记录执行操作。此示例将 Acme 客户添加到 Salesforce。首先创建一个客户 sObject,然后将其作为参数传递给 insert 语句,该语句会将记录保留在 Salesforce 中。

// Create the account sObject
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
// Insert the account by using DML
insert acct;

DML 语句

以下是可用的 DML 语句。

  • insert
  • update
  • upsert
  • delete
  • undelete
  • merge

每个 DML 语句接受单个 sObject 或 sObject 的列表(或数组)。对 sObject 列表进行操作是处理记录更有效的方法。

除了个别语句外,所有这些语句都是熟悉的数据库操作。Upsertmerge 语句是 Salesforce 特有的,使用非常方便。

通过 upsert DML 操作在单个语句中创建新记录并更新 sObject 记录,使用指定字段来确定现有对象的存在,如果没有指定字段,则使用 ID 字段。

Merge 语句将最多三个相同 sObject 类型的记录合并到其中一个记录中,同时删除其他记录,并重新设置关联记录的父级。

自动分配给新记录的 ID 字段

插入记录时,系统会为每条记录分配一个 ID。除了将 ID 值保留在数据库之外,ID 值还会自动填充到您在 DML 调用中用作参数的 sObject 变量上。

此示例展示了如何获取与插入客户对应的 sObject 上的 ID。

// Create the account sObject
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
// Insert the account by using DML
insert acct;
// Get the new ID on the inserted sObject argument
ID acctID = acct.Id;
// Display this ID in the debug log
System.debug('ID = ' + acctID);
// Debug log result (the ID will be different in your case)
// DEBUG|ID = 001D000000JmKkeIAF







备注

进阶学习

由于示例中的 sObject 变量包含 DML 调用后的 ID,您可以重用 sObject 变量来执行其他 DML 操作,例如更新操作,因为系统能够通过匹配 ID 的方式将 sObject 变量映射到其对应的记录上。

您可以从数据库中检索记录以获取其字段,包括 ID 字段,但这不能通过 DML 来完成。您需要使用 SOQL 编写查询语句。您会在另一个单元中了解关于 SOQL 的更多信息。

批量 DML

您可以对单个 sObject 或批量对 sObject 列表执行 DML 操作。推荐使用批量 DML 操作的方法,因为它有助于避免达到调控器限制,例如每个 Apex 事务有 150 条语句的 DML 限制。设置此限制的目的是确保公平访问 Lightning 平台中的共享资源。对 sObject 列表执行 DML 操作算作一个 DML 语句,而不是每个 sObject 一个语句。

本示例通过在一次调用中插入联系人列表的方式,达到批量插入联系人的目的。然后,样本也会批量更新这些联系人。

  1. 使用匿名 Apex 在 Developer Console 中执行此代码片段。
    // Create a list of contacts
    List<Contact> conList = new List<Contact> {
        new Contact(FirstName='Joe',LastName='Smith',Department='Finance'),
            new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'),
            new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'),
            new Contact(FirstName='Kim',LastName='Shain',Department='Education')};
    // Bulk insert all contacts with one DML call
    insert conList;
    // List to hold the new contacts to update
    List<Contact> listToUpdate = new List<Contact>();
    // Iterate through the list and add a title only
    //   if the department is Finance
    for(Contact con : conList) {
        if (con.Department == 'Finance') {
            con.Title = 'Financial analyst';
            // Add updated contact sObject to the list.
            listToUpdate.add(con);
        }
    }
    // Bulk update all contacts with one DML call
    update listToUpdate;
  2. 检查最近在组织中创建的联系人。
    财务部门的两个联系人的职务应该是 Financial analyst(财务分析师)。

更新插入记录

如果列表中同时包含新记录和已有记录,您可以使用 upsert 语句处理列表中所有记录的插入和更新操作。upsert 语句可避免创建重复记录,您不需要判断哪些是已有记录,从而为您节省时间。

Upsert 语句通过比较某个字段的值,将 sObject 与现有记录进行匹配。如果调用此语句时没有指定字段,则 upsert 语句会使用 sObject ID 来匹配 Salesforce 中的已有记录。或者,您可以指定一个用于匹配的字段。对于自定义对象,指定一个标记为外部 ID 的自定义字段。对于标准对象,可以指定任何将 idLookup 属性设置为 true 的字段。例如,“联系人”或“用户”的“电子邮件”字段设置了 idLookup 属性。要检查字段属性,请参见 Object Reference for Salesforce and Lightning Platform(Salesforce 和 Lightning 平台的对象引用)

upsert 语法

upsert sObject | sObject[]


upsert sObject | sObject[] field

可选字段是字段令牌。例如,要指定 MyExternalID 字段,语句是:

upsert sObjectList Account.Fields.MyExternalId;

upsert 使用 sObject 记录的主键 (ID)、idLookup 字段或外部 ID 字段来确定是应该创建新记录还是更新已有记录:

  • 如果主键不匹配,则创建新对象记录。
  • 如果主键匹配一次,则更新已有对象记录。
  • 如果主键匹配多次,则会产生错误,并且不插入也不更新对象记录。

此示例展示了如何通过 upsert 更新已有联系人记录并在单次调用中插入新联系人。调用 upsert 更新了已有联系人 Josh,并插入了新联系人 Kathy。

备注

备注

upsert 调用时使用 ID 匹配第一个联系人。在 upsert 调用中重用 josh 变量。该变量使用了上一次 insert 调用的记录 ID 来填充,因此在本例中不需要显式设置 ID。

  1. 在 Developer Console 的 Execute Anonymous(执行匿名) 窗口中执行此代码片段。
    // Insert the Josh contact
    Contact josh = new Contact(FirstName='Josh',LastName='Kaplan',Department='Finance');
    insert josh;
    // Josh's record has been inserted
    //   so the variable josh has now an ID
    //   which will be used to match the records by upsert
    josh.Description = 'Josh\'s record has been updated by the upsert operation.';
    // Create the Kathy contact, but don't persist it in the database
    Contact kathy = new Contact(FirstName='Kathy',LastName='Brown',Department='Technology');
    // List to hold the new contacts to upsert
    List<Contact> contacts = new List<Contact> { josh, kathy };
    // Call upsert
    upsert contacts;
    // Result: Josh is updated and Kathy is created.
  2. 检查组织中的所有联系人。
    组织将只有一个 Josh Kaplan 记录,而不是两个,因为 upsert 操作发现了已有的记录并对其进行更新,而不是创建一个新的联系人记录。您还会看到名为 Kathy Brown 的联系人记录。

或者,您可以指定一个用于匹配记录的字段。本示例使用了“联系人”的“电子邮件”字段,因为它设置了 idLookup 属性。本示例插入了联系人 Jane Smith,创建了第二个联系人 sObject,并用相同电子邮件来填充 sObject,然后调用 upsert 语句通过电子邮件字段匹配的方式来更新联系人。

备注

备注

如果该示例使用的是 insert 而不是 upsert 语句,则会重复插入联系人 Jane Smith。

  1. 在 Developer Console 的 Execute Anonymous(执行匿名) 窗口中执行此代码片段。
    Contact jane = new Contact(FirstName='Jane',
                             LastName='Smith',
                             Email='jane.smith@example.com',
                             Description='Contact of the day');
    insert jane;
    // 1. Upsert using an idLookup field
    // Create a second sObject variable.
    // This variable doesn’t have any ID set.
    Contact jane2 = new Contact(FirstName='Jane',
                             LastName='Smith',
                             Email='jane.smith@example.com',
                             Description='Prefers to be contacted by email.');
    // Upsert the contact by using the idLookup field for matching.
    upsert jane2 Contact.fields.Email;
    // Verify that the contact has been updated
    System.assertEquals('Prefers to be contacted by email.',
                       [SELECT Description FROM Contact WHERE Id=:jane.Id].Description);
  2. 检查组织中的所有联系人。
    组织将只有一位 Jane Smith 联系人应用了更新的描述。

删除记录

通过 delete 语句删除永久记录。已删除的记录不会从 Lightning 平台永久删除,而是在回收站中保存 15 天,您可以从回收站中恢复记录。

此示例展示了如何删除 Smith 姓氏的所有联系人。如果您运行了批量 DML 操作示例,那么组织中应该有两个 Smith 姓氏的联系人。使用匿名 Apex 在 Developer Console 中执行此代码片段,然后确认不存在 Smith 姓氏的联系人。

Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName='Smith'];
delete contactsDel;
备注

备注

此代码片段包括检索联系人的查询(SOQL 查询)。您会在另一个单元中了解关于 SOQL 的更多信息。

DML 语句异常

如果 DML 操作失败,将返回 DmlException 类型的异常。您可以在代码中捕获异常以处理错误情况。

该示例产生了 DmlException 异常,原因是插入了一个不包含必填名称字段的客户。在 catch 块中捕获了异常。

try {
    // This causes an exception because
    //   the required Name field is not provided.
    Account acct = new Account();
    // Insert the account
    insert acct;
} catch (DmlException e) {
    System.debug('A DML exception has occurred: ' +
                e.getMessage());
}



数据库方法

Apex 包含了内置的数据库类,该类提供执行 DML 操作和镜像 DML 语句对应项的方法。

数据库方法是静态的,并在类名上调用。

  • Database.insert()
  • Database.update()
  • Database.upsert()
  • Database.delete()
  • Database.undelete()
  • Database.merge()

与 DML 语句不同,数据库方法有一个可选的 allOrNone 参数,它允许您指定操作是否可以部分成功。当该参数设置为 false 时,如果部分记录集发生错误,将提交成功的记录,并为失败的记录返回错误。另外,部分成功选项不会抛出异常。

这就是在 allOrNone 设置为 false 的情况下调用 insert 方法的方式。

Database.insert(recordList, false);

数据库方法返回包含每个记录的成功或失败信息的结果对象。例如,insert 和 update 操作都会返回 Database.SaveResult 对象数组。

Database.SaveResult[] results = Database.insert(recordList, false);
备注

备注

upsert 操作返回 Database.UpsertResult 对象,delete 操作返回 Database.DeleteResult 对象。

默认情况下,allOrNone 参数为 true,表示数据库方法的行为与其对应的 DML 语句相同,并且在遇到失败时会抛出异常。

以下两条语句等同于 insert recordList; 语句。

Database.insert(recordList);

和:

Database.insert(recordList, true);







备注

进阶学习

除了这些方法之外,数据库类还包含不作为 DML 语句提供的方法。例如,用于事务控制和回滚的方法、用于清空回收站的方法以及与 SOQL 查询相关的方法。您会在另一个单元中了解关于 SOQL 的更多信息。

示例:插入部分成功的记录

让我们看一个使用数据库方法的示例。本示例基于批量 DML 操作示例,将 DML 语句替换为数据库方法。使用部分成功选项调用 Database.insert() 方法。如果列表中的联系人未设置任何字段,将会导致错误,原因是不包含必填字段 LastName,无法保存该联系人。提交了三个联系人,没有任何字段的联系人会生成错误。本示例最后部分遍历返回的结果并将调试消息写入调试日志。

  1. 在 Developer Console 的 Execute Anonymous(执行匿名) 窗口中执行此示例。
    // Create a list of contacts
    List<Contact> conList = new List<Contact> {
            new Contact(FirstName='Joe',LastName='Smith',Department='Finance'),
            new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'),
            new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'),
            new Contact()};
    // Bulk insert all contacts with one DML call
    Database.SaveResult[] srList = Database.insert(conList, false);
    // Iterate through each returned result
    for (Database.SaveResult sr : srList) {
        if (sr.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully inserted contact. Contact ID: ' + sr.getId());
        } else {
            // Operation failed, so get all errors
            for(Database.Error err : sr.getErrors()) {
                System.debug('The following error has occurred.');
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('Contact fields that affected this error: ' + err.getFields());
    	 }
        }
    }
  2. 验证调试消息(对筛选器使用 DEBUG 关键字)。
    应报告一个失败,同时应该插入了三个联系人。

您选择使用 DML 语句还是数据库方法?

  • 如果您希望将批量 DML 处理期间发生的任何错误作为立即中断控制流的 Apex 异常抛出,请使用 DML 语句(使用 try...catch 块)。这种行为与大多数数据库过程化语言中处理异常的方式类似。
  • 如果您希望允许批量 DML 操作部分成功,请使用数据库类方法 — 如果记录失败,DML 操作的其余部分仍然可以成功。然后,应用程序可以检查被拒记录,并执行重试操作。使用 Database 类方法可以编写不抛出 DML 异常错误的代码。相反,您的代码可以使用适当的结果数组来判断成功或失败。与 DML 语句类似,数据库方法还包括支持抛出异常的语法。

创建和管理通过关系相互关联的记录。

插入相关记录

如果已经在两个对象之间定义了关系,例如查找或大纲-细节关系,您可以插入与现有记录相关的记录。通过外键 ID 让记录与记录之间产生关联。例如,如果插入一个新联系人,您可以通过设置 AccountId 字段的值来指定该联系人的相关客户记录。

此示例展示了如何通过设置联系人的 AccountId 字段将联系人添加到客户(相关记录)。联系人和客户之间通过查找关系建立关联。

  1. 在 Developer Console 的 Anonymous Apex(匿名 Apex)窗口中执行此代码片段。
    Account acct = new Account(Name='SFDC Account');
    insert acct;
    // Once the account is inserted, the sObject will be
    // populated with an ID.
    // Get this ID.
    ID acctID = acct.ID;
    // Add a contact to this account.
    Contact mario = new Contact(
        FirstName='Mario',
        LastName='Ruiz',
        Phone='415.555.1212',
        AccountId=acctID);
    insert mario;
  2. 检查组织中的联系人。
    已创建新客户(SFDC 客户),客户联系人相关列表中包含了联系人 Mario Ruiz。

更新相关记录

不能重复调用 DML 操作对相关记录的字段进行更新,需要执行单独调用 DML 操作。例如,如果插入一个新联系人,您可以通过设置 AccountId 字段的值来指定该联系人的相关客户记录。但是,如果不使用单独调用 DML 操作来更新客户,客户名称也不会更改。同样,在更新联系人时,如果您还想更新联系人的相关客户,则必须进行两次 DML 调用操作。以下示例使用两个 update 语句更新联系人及其相关客户。

// Query for the contact, which has been associated with an account.
Contact queriedContact = [SELECT Account.Name
                          FROM Contact
                          WHERE FirstName = 'Mario' AND LastName='Ruiz'
                          LIMIT 1];
// Update the contact's phone number
queriedContact.Phone = '(415)555-1213';
// Update the related account industry
queriedContact.Account.Industry = 'Technology';
// Make two separate calls
// 1. This call is to update the contact's phone.
update queriedContact;
// 2. This call is to update the related account's Industry field.
update queriedContact.Account;

删除相关记录

Delete 操作支持级联删除。在每条子记录都允许删除的情况下删除父对象,其子对象也会自动删除。

例如,删除您之前创建的客户(SFDC 客户),其相关联系人也会被删除。

  1. 在 Developer Console 的 Anonymous Apex(匿名 Apex)窗口中执行此代码片段。
    Account[] queriedAccounts = [SELECT Id FROM Account WHERE Name='SFDC Account'];
    delete queriedAccounts;
  2. 检查组织中的客户和联系人。
    您可以看到客户及其相关联系人已删除。

关于事务

DML 操作在事务中执行。一个事务中的所有 DML 操作要么成功完成,要么如果在一个操作中发生错误,则整个事务将回滚并且不向数据库提交任何数据。事务的边界可以是触发器、类方法、匿名代码块、Apex 页面或自定义 Web 服务方法。例如,如果触发器或类创建了两个客户并更新了一个联系人,并且由于验证规则失败而导致联系人更新失败,则整个事务将回滚并且不在 Salesforce 中保留任何客户。

资源

在 Salesforce 帮助中分享 Trailhead 反馈

我们很想听听您使用 Trailhead 的经验——您现在可以随时从 Salesforce 帮助网站访问新的反馈表单。

了解更多 继续分享反馈