通过 DML 操作记录
学习目标
完成本单元后,您将能够:
- 使用 DML 插入、更新和删除记录。
- 批量执行 DML 语句。
- 使用 upsert 插入或更新记录。
- 捕获 DML 异常。
- 使用数据库方法插入带有部分成功选项的新记录并处理结果。
- 清楚何时使用 DML 语句以及何时使用数据库方法。
- 对相关记录执行 DML 操作。
通过 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 列表进行操作是处理记录更有效的方法。
除了个别语句外,所有这些语句都是熟悉的数据库操作。Upsert
及 merge
语句是 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
批量 DML
您可以对单个 sObject 或批量对 sObject 列表执行 DML 操作。推荐使用批量 DML 操作的方法,因为它有助于避免达到调控器限制,例如每个 Apex 事务有 150 条语句的 DML 限制。设置此限制的目的是确保公平访问 Lightning 平台中的共享资源。对 sObject 列表执行 DML 操作算作一个 DML 语句,而不是每个 sObject 一个语句。
本示例通过在一次调用中插入联系人列表的方式,达到批量插入联系人的目的。然后,样本也会批量更新这些联系人。
- 使用匿名 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;
- 检查最近在组织中创建的联系人。
财务部门的两个联系人的职务应该是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。
- 在 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.
- 检查组织中的所有联系人。
组织将只有一个 Josh Kaplan 记录,而不是两个,因为 upsert 操作发现了已有的记录并对其进行更新,而不是创建一个新的联系人记录。您还会看到名为 Kathy Brown 的联系人记录。
或者,您可以指定一个用于匹配记录的字段。本示例使用了“联系人”的“电子邮件”字段,因为它设置了 idLookup 属性。本示例插入了联系人 Jane Smith,创建了第二个联系人 sObject,并用相同电子邮件来填充 sObject,然后调用 upsert
语句通过电子邮件字段匹配的方式来更新联系人。
- 在 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);
- 检查组织中的所有联系人。
组织将只有一位 Jane Smith 联系人应用了更新的描述。
删除记录
通过 delete
语句删除永久记录。已删除的记录不会从 Lightning 平台永久删除,而是在回收站中保存 15 天,您可以从回收站中恢复记录。
此示例展示了如何删除 Smith 姓氏的所有联系人。如果您运行了批量 DML 操作示例,那么组织中应该有两个 Smith 姓氏的联系人。使用匿名 Apex 在 Developer Console 中执行此代码片段,然后确认不存在 Smith 姓氏的联系人。
Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName='Smith']; delete contactsDel;
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);
默认情况下,allOrNone 参数为 true
,表示数据库方法的行为与其对应的 DML 语句相同,并且在遇到失败时会抛出异常。
以下两条语句等同于 insert recordList;
语句。
Database.insert(recordList);
和:
Database.insert(recordList, true);
示例:插入部分成功的记录
让我们看一个使用数据库方法的示例。本示例基于批量 DML 操作示例,将 DML 语句替换为数据库方法。使用部分成功选项调用 Database.insert()
方法。如果列表中的联系人未设置任何字段,将会导致错误,原因是不包含必填字段 LastName,无法保存该联系人。提交了三个联系人,没有任何字段的联系人会生成错误。本示例最后部分遍历返回的结果并将调试消息写入调试日志。
- 在 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()); } } }
- 验证调试消息(对筛选器使用 DEBUG 关键字)。
应报告一个失败,同时应该插入了三个联系人。
您选择使用 DML 语句还是数据库方法?
- 如果您希望将批量 DML 处理期间发生的任何错误作为立即中断控制流的 Apex 异常抛出,请使用 DML 语句(使用
try...catch
块)。这种行为与大多数数据库过程化语言中处理异常的方式类似。
- 如果您希望允许批量 DML 操作部分成功,请使用数据库类方法 — 如果记录失败,DML 操作的其余部分仍然可以成功。然后,应用程序可以检查被拒记录,并执行重试操作。使用 Database 类方法可以编写不抛出 DML 异常错误的代码。相反,您的代码可以使用适当的结果数组来判断成功或失败。与 DML 语句类似,数据库方法还包括支持抛出异常的语法。
处理相关记录
创建和管理通过关系相互关联的记录。
插入相关记录
如果已经在两个对象之间定义了关系,例如查找或大纲-细节关系,您可以插入与现有记录相关的记录。通过外键 ID 让记录与记录之间产生关联。例如,如果插入一个新联系人,您可以通过设置 AccountId
字段的值来指定该联系人的相关客户记录。
此示例展示了如何通过设置联系人的 AccountId
字段将联系人添加到客户(相关记录)。联系人和客户之间通过查找关系建立关联。
- 在 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;
- 检查组织中的联系人。
已创建新客户(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 客户),其相关联系人也会被删除。
- 在 Developer Console 的 Anonymous Apex(匿名 Apex)窗口中执行此代码片段。
Account[] queriedAccounts = [SELECT Id FROM Account WHERE Name='SFDC Account']; delete queriedAccounts;
- 检查组织中的客户和联系人。
您可以看到客户及其相关联系人已删除。
关于事务
DML 操作在事务中执行。一个事务中的所有 DML 操作要么成功完成,要么如果在一个操作中发生错误,则整个事务将回滚并且不向数据库提交任何数据。事务的边界可以是触发器、类方法、匿名代码块、Apex 页面或自定义 Web 服务方法。例如,如果触发器或类创建了两个客户并更新了一个联系人,并且由于验证规则失败而导致联系人更新失败,则整个事务将回滚并且不在 Salesforce 中保留任何客户。
资源
- Apex 开发人员指南
- Apex 开发人员指南:在 Apex 中处理数据
- Apex 开发人员指南:通过 DML 添加和检索数据
- Apex 开发人员指南:数据库类
- Apex 开发人员指南:例外语句