学习目标
完成本单元后,您将能够:
- 使用DML插入,更新和删除记录。
- 批量执行DML语句。
- 使用upsert来插入或更新记录。
- 捕获一个DML异常。
- 使用数据库方法插入具有部分成功选项的新记录并处理结果。
- 知道何时使用DML语句以及何时使用数据库方法。
- 对相关记录执行DML操作
用DML操作记录
使用数据操作语言(简称DML)在Salesforce中创建和修改记录。 DML通过提供简单的语句来插入,更新,合并,删除和恢复记录,提供了一种直接管理记录的方法。
由于Apex是一种以数据为中心的语言,并保存在Force.com平台上,因此可直接访问Salesforce中的数据。与其他需要额外设置以连接数据源的编程语言不同,使用Apex DML,管理记录变得非常简单!通过调用DML语句,您可以快速对Salesforce记录执行操作。
此示例将Acme客户添加到Salesforce。首先创建一个客户sObject,然后将其作为参数传递给insert语句,该语句在Salesforce中保留该记录。
// 创建客户sObject
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
// 通过使用DML插入客户
insert acct;
DML语句
以下DML语句可用。
- insert
- update
- upsert
- delete
- undelete
- merge
每个DML语句接受一个单独的sObject或一个sObject的列表(或数组)。在sObjects列表上操作是处理记录的更有效的方法。
除了一对,所有这些陈述都是熟悉的数据库操作。 upsert和merge语句是Salesforce特有的,可以非常方便。
upsert DML操作创建新记录,并在单个语句中更新sObject记录,使用指定的字段确定现有对象的存在,或者在没有指定字段的情况下使用ID字段。
合并语句将最多三个相同sObject类型的记录合并到其中一个记录中,删除其他记录,并将所有相关记录重新归类。
ID字段自动分配到新记录
插入记录时,系统为每个记录分配一个ID。除了在数据库中保存ID值之外,还可以在用作DML调用中的参数的sObject变量上自动填充ID值。
这个例子展示了如何获得对应于插入客户的sObject的ID。
// 创建客户sObject
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
// 通过使用DML插入客户
insert acct;
// 获取插入的sObject参数的新ID
ID acctID = acct.Id;
// 在调试日志中显示此ID
System.debug('ID = ' + acctID);
// 调试日志结果(在您的情况下ID将不同)
// DEBUG|ID = 001D000000JmKkeIAF
超越基础
由于示例中的sObject变量包含DML调用后的ID,因此您可以重新使用此sObject变量来执行进一步的DML操作(如更新),因为系统可以通过匹配ID来将sObject变量映射到其对应的记录。
您可以从数据库检索记录以获取其字段,包括ID字段,但这不能用DML完成。您需要使用SOQL编写查询。您将在另一个单元学习SOQL。
批量 DML
您可以在单个sObject上执行DML操作,也可以在sObjects列表上批量执行DML操作。执行批量DML操作是推荐的方法,因为它有助于避免触及限额,例如每个Apex交易的DML限制为150条语句。这个限制是为了确保公平地访问Force.com多租户平台中的共享资源。在sObjects列表上执行DML操作会计算为列表中所有sObjects的一个DML语句,而不是每个sObject的一个语句。
此示例通过在一次调用中插入联系人列表来批量插入联系人。该示例然后批量更新这些联系人。
- 在开发者控制台中使用Anonymous Apex执行此代码段。
// 创建联系人列表 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')}; // 用一个DML调用批量插入所有联系人 insert conList; // List来保存新的联系人进行更新 List<Contact> listToUpdate = new List<Contact>(); // 遍历列表并仅添加标题 // 如果部门是财务部门 for(Contact con : conList) { if (con.Department == 'Finance') { con.Title = 'Financial analyst'; // Add updated contact sObject to the list. listToUpdate.add(con); } } // 将更新的联系人sObject添加到列表中。 update listToUpdate;
- 检查最近在您的组织中创建的联系人。
两位在财务部门的联系人应该有他们的头衔,财务分析师。
插入记录
如果您有一个包含新记录和现有记录混合的列表,则可以使用upsert语句来处理对列表中所有记录的插入和更新。 Upsert有助于避免重复记录的创建,并且可以节省时间,因为您不必先确定哪些记录存在。
upsert语句通过比较一个字段的值来将sObjects与现有记录进行匹配。如果调用此语句时未指定字段,则upsert语句将使用sObject的ID将sObject与Salesforce中的现有记录进行匹配。或者,您可以指定一个字段用于匹配。对于自定义对象,请指定标记为外部标识的自定义字段。对于标准对象,可以指定任何idLookup属性设置为true的字段。例如,联系人或用户的电子邮件字段设置了idLookup属性。要检查字段的属性,请参阅Salesforce和Force.com的对象参考。
Upsert语法
upsert sObject | sObject[]
upsert sObject | sObject[] field
可选字段是字段标记。例如,要指定MyExternalID字段,该语句是:
upsert sObjectList Account.Fields.MyExternalId;
Upsert使用sObject记录的主键(ID),idLookup字段或外部ID字段来确定是否应该创建新记录或更新现有记录:
- 如果密钥不匹配,则创建一个新的对象记录。
- 如果密钥匹配一次,则更新现有对象记录。
- 如果密钥匹配多次,则会生成错误,并且不会插入或更新对象记录。
这个例子显示了upsert如何更新一个现有的联系人记录,并在一个呼叫中插入一个新的联系人。这个upsert调用更新现有的Josh联系人并插入一个新的联系人Kathy。
- 在开发者控制台的“执行匿名”窗口中执行此代码段。
//插入Josh联系人 Contact josh = new Contact(FirstName='Josh',LastName='Kaplan',Department='Finance'); insert josh; // Josh's 的记录已被插入 // 所以变量josh现在有一个ID // 将用于通过upsert匹配记录 josh.Description = 'Josh\'s record has been updated by the upsert operation.'; // 创建Kathy联系人,但不要将其保存在数据库中 Contact kathy = new Contact(FirstName='Kathy',LastName='Brown',Department='Technology'); // 列举以保持新联系人upsert List<Contact> contacts = new List<Contact> { josh, kathy }; // 调用upsert upsert contacts; // 结果:Josh被更新,Kathy被创建。
- 检查组织中的所有联系人。
您的组织只有一个Josh Kaplan记录,而不是两个,因为upsert操作找到了现有记录并进行了更新,而不是创建新的联系人记录。凯西·布朗的一个联系记录也将在那里。或者,您可以指定一个字段用于匹配记录。此示例使用Contact上的电子邮件字段,因为它具有idLookup属性集。该示例插入Jane Smith联系人,并创建第二个Contact sObject,使用相同的电子邮件填充它,然后使用电子邮件字段进行匹配以调用upsert以更新联系人。
- 在开发者控制台的“执行匿名”窗口中执行此代码段。
Contact jane = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Contact of the day'); insert jane; // 1.使用idLookup字段的Upsert // 创建第二个sObject变量。 // 这个变量没有任何ID设置。 Contact jane2 = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Prefers to be contacted by email.'); // 通过使用idLookup字段进行匹配来提升联系人。 upsert jane2 Contact.fields.Email; // 确认联系人已更新 System.assertEquals('Prefers to be contacted by email.', [SELECT Description FROM Contact WHERE Id=:jane.Id].Description);
- 检查组织中的所有联系人。
您的组织将只有一个Jane Smith联系更新后的描述。
删除记录
您可以使用delete语句删除持久记录。删除的记录不会从Force.com永久删除,但是它们可以从恢复位置放置在回收站中15天。
此示例显示如何删除姓氏为Smith的所有联系人。如果您已经为批量DML运行了样本,那么您的组织应该与Smith的姓氏有两个联系。在开发人员控制台中使用匿名Apex执行此代码段,然后验证是否没有与姓氏Smith的联系人。
Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName='Smith'];
delete contactsDel;
DML语句异常
如果DML操作失败,则返回DmlException类型的异常。您可以在代码中捕获异常以处理错误情况。
这个例子产生了一个DmlException,因为它试图插入一个没有必需的Name字段的客户。 catch块中捕获到异常。
try {
// 这会导致异常
// 不提供必需的名称字段。
Account acct = new Account();
// 插入客户
insert acct;
} catch (DmlException e) {
System.debug('A DML exception has occurred: ' +
e.getMessage());
}
数据库方法
这些数据库方法是静态的,并在类名称上调用。
- Database.insert()
- Database.update()
- Database.upsert()
- Database.delete()
- Database.undelete()
- Database.merge()
与DML语句不同,数据库方法有一个可选的allOrNone参数,允许您指定操作是否应该部分成功。当此参数设置为false时,如果在部分记录集上发生错误,则成功的记录将被提交,并且将为失败的记录返回错误。另外,部分成功选项也不会引发异常。
这是你如何调用allOrNone设置为false的插入方法。
Database.insert(recordList, false);
Database.SaveResult[] results = Database.insert(recordList, false);
Database.insert(recordList);
Database.insert(recordList, true);
超越基础
除了这些方法外,Database类还包含不作为DML语句提供的方法。例如,用于事务控制和回滚的方法,用于清空回收站以及与SOQL查询相关的方法。您将在另一个单元学习SOQL。
示例:以部分成功插入记录
我们来看看使用数据库方法的例子。此示例基于批量DML示例,但是用Database方法替换了DML语句。用部分成功选项调用Database.insert()方法。列表中的一个联系人没有任何有意使用的字段,并且会导致错误,因为如果没有所需的姓氏字段,则无法保存联系人。三个联系人被提交,没有任何字段的联系人会产生错误。本示例的最后一部分遍历返回的结果并将调试消息写入调试日志。
- 在开发者控制台的“执行匿名”窗口中执行此示例。
// 创建联系人列表 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()}; // 用一个DML调用批量插入所有联系人 Database.SaveResult[] srList = Database.insert(conList, false); // 遍历每个返回的结果 for (Database.SaveResult sr : srList) { if (sr.isSuccess()) { // 操作成功,所以得到处理记录的ID System.debug('Successfully inserted contact. Contact ID: ' + sr.getId()); } else { // 操作失败,所以得到所有的错误 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异常(通过使用try。。catch块)引发,请使用DML语句。这种行为类似于大多数数据库过程语言中处理异常的方式。
- 如果要允许批量DML操作的部分成功,请使用Database类方法 – 如果记录失败,则DML操作的其余部分仍可能成功。然后您的应用程序可以检查被拒绝的记录,并可能重试该操作。使用此表单时,您可以编写不会抛出DML异常错误的代码。相反,您的代码可以使用适当的结果数组来判断成功或失败。请注意,数据库方法还包括一个支持抛出异常的语法,类似于DML语句。