Salesforce Apex (基础5)SOSL

学习目标

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

  • 描述SOSL和SOQL之间的区别。
  • 使用SOSL查询搜索跨多个对象的字段。
  • 使用开发者控制台中的查询编辑器执行SOSL查询。

编写SOSL查询

Salesforce对象搜索语言(SOSL)是一种Salesforce搜索语言,用于在记录中执行文本搜索。使用SOSL在Salesforce中跨多个标准和自定义对象记录搜索字段。 SOSL类似于Apache Lucene。
向Apex添加SOSL查询非常简单 – 您可以直接在您的Apex代码中嵌入SOSL查询。当SOSL嵌入在Apex中时,它被称为在线SOSL。

这是SOSL查询的一个例子,该查询搜索具有任何字段“SFDC”的客户和联系人。

List<List<SObject>> searchList = [FIND 'SFDC' IN ALL FIELDS 
                                      RETURNING Account(Name), Contact(FirstName,LastName)];

SOQL与SOSL的差异与相似性

与SOQL一样,SOSL允许您搜索组织的记录以获取特定信息。与一次只能查询一个标准或自定义对象的SOQL不同,单个SOSL查询可以搜索所有对象。

另一个区别是SOSL匹配基于单词匹配的字段,而默认情况下SOQL执行完全匹配(当不使用通配符时)。例如,在SOSL中搜索“Digital”会返回字段值为“Digital”或“The Digital Company”的记录,但SOQL只返回字段值为“Digital”的记录。

SOQL和SOSL是两种不同语法的独立语言。每种语言都有一个独特的用例:

  • 使用SOQL检索单个对象的记录。
  • 使用SOSL跨多个对象搜索字段。 SOSL查询可以搜索对象上的大多数文本字段。

先决条件

本单位的一些查询希望组织拥有账户和联系人。如果您还没有在SOQL单元中创建样本数据,请在本单元中创建样本数据。否则,您可以跳过在本节中创建示例数据。

  1. 在开发人员控制台中,从“调试”菜单中打开“执行匿名”窗口。
  2. 在窗口中插入下面的代码片段,然后点击执行。
// 添加客户和相关联系人
Account acct = new Account(
    Name='SFDC Computing',
    Phone='(415)555-1212',
    NumberOfEmployees=50,
    BillingCity='San Francisco');
insert acct;

// 一旦插入客户,sObject将填充一个ID。
// 获取这个ID
ID acctID = acct.ID;

// 添加一个联系人到这个客户。
Contact con = new Contact(
    FirstName='Carol',
    LastName='Ruiz',
    Phone='(415)555-1212',
    Department='Wingo',
    AccountId=acctID);
insert con;

// 添加没有联系人的客户
Account acct2 = new Account(
    Name='The SFDC Query Man',
    Phone='(310)555-1213',
    NumberOfEmployees=50,
    BillingCity='Los Angeles',
    Description='Expert in wing technologies.');
insert acct2;

使用查询编辑器

开发者控制台提供查询编辑器控制台,使您可以运行SOSL查询和查看结果。查询编辑器提供了一种快速检查数据库的方法。在将它们添加到您的Apex代码之前,测试您的SOSL查询是一个好方法。当您使用查询编辑器时,您只需提供没有围绕它的Apex代码的SOSL语句。

让我们尝试运行以下SOSL示例:

  1. 在开发者控制台中,点击 Query Editor 标签。
  2. 将以下内容复制并粘贴到查询编辑器下的第一个框中,然后单击 Execute.
FIND {Wingo} IN ALL FIELDS RETURNING Account(Name), Contact(FirstName,LastName,Department)
您组织中符合标准的所有客户和联系人记录都将显示在“查询结果”部分中作为包含字段的行。结果按每个对象(客户或联系人)的选项卡进行分组。 SOSL查询返回具有与Wingo匹配的字段的记录。根据我们的样本数据,只有联系人具有Wingo值的字段,因此此联系人将被返回。

注意

查询编辑器和API中的搜索查询必须用大括号({Wingo})括起来。相反,在Apex中,搜索查询包含在单引号(’Wingo’)内。

基本的SOSL语法

SOSL允许您指定以下搜索条件:

  • 文本表达(单个单词或短语)来搜索
  • 要搜索的字段的范围
  • 要检索的对象和字段的列表
  • 在源对象中选择行的条件

这是一个基本的SOSL查询语法:

FIND 'SearchQuery' [IN SearchGroup] [RETURNING ObjectsAndFields]

SearchQuery是要搜索的文本(单个单词或短语)。搜索项可以与逻辑运算符(AND,OR)和括号组合。此外,搜索字词可以包含通配符(*,?)。 *通配符在搜索项的中间或末尾匹配零个或多个字符。这个?通配符只匹配搜索项中间或末尾的一个字符。

文本搜索不区分大小写。例如,搜索客户,客户或CUSTOMER都会返回相同的结果。

SearchGroup是可选的。这是搜索领域的范围。如果未指定,则默认搜索范围是所有字段。 SearchGroup可以采取以下值之一。

  • ALL FIELDS
  • NAME FIELDS
  • EMAIL FIELDS
  • PHONE FIELDS
  • SIDEBAR FIELDS

ObjectsAndFields是可选的。它是在搜索结果中返回的信息 – 一个或多个sObjects的列表,并且在每个sObject中,包含一个或多个字段的列表,并带有可筛选的值。如果未指定,则搜索结果包含找到的所有对象的ID。

单词和短语

SearchQuery包含两种类型的文本:

  • Single Word— 单个单词,如test或hello。 SearchQuery中的单词由空格,标点符号和从字母到数字的变化(反之亦然)分隔。单词总是不区分大小写的。
  • Phrase— 由双引号包围的单词和空格的集合,如“约翰史密斯”。多个单词可以与逻辑和分组操作符组合在一起,形成一个更复杂的查询。

搜索示例

要了解SOSL搜索的工作原理,让我们使用不同的搜索字符串来查看输出是基于我们的样本数据。此表列出了各种示例搜索字符串和SOSL搜索结果。

在所有领域搜索: 搜索说明 匹配的记录和字段
查询 此搜索将返回其所有字段都包含文本的所有记录:The和Query。搜索字词中的单词顺序无关紧要。 客户:SFDC查询人(名称字段匹配)
Wingo OR Man 该搜索使用OR逻辑运算符。它返回包含Wingo单词的字段的记录或包含SFDC单词的字段的记录。 联系人:Carol Ruiz,部门:’Wingo’
客户:SFDC查询人(名称字段匹配)
1212 此搜索将返回其所有字段包含1212字的记录。以-1212结尾的电话字段被匹配,因为1212被短划线分隔时被认为是单词。客户:SFDC查询人,电话:’(415)555-1212′ 联系人:Carol Ruiz,电话:’(415)555-1212′
wing* 这是一个通配符搜索。此搜索将返回具有以wing开头的字段值的所有记录。

账号:SFDC查询员,描述: ‘Expert in wing technologies.’

SOSL Apex示例

此示例显示如何在Apex中运行SOSL查询。这个SOSL查询通过使用OR逻辑运算符来组合两个搜索项 – 它在任何字段中搜索Wingo或SFDC。此示例返回所有示例客户,因为它们每个都有一个包含其中一个单词的字段。 SOSL搜索结果以列表形式返回。每个列表包含返回记录的数组。在这种情况下,列表有两个元素。在索引0处,列表包含客户数组。在索引1处,列表包含联系人数组。

在开发者控制台的“执行匿名”窗口中执行此代码段。接下来,检查调试日志以验证是否返回所有记录。

List<List<sObject>> searchList = [FIND 'Wingo OR SFDC' IN ALL FIELDS 
                   RETURNING Account(Name),Contact(FirstName,LastName,Department)];
Account[] searchAccounts = (Account[])searchList[0];
Contact[] searchContacts = (Contact[])searchList[1];

System.debug('Found the following accounts.');
for (Account a : searchAccounts) {
    System.debug(a.Name);
}

System.debug('Found the following contacts.');
for (Contact c : searchContacts) {
    System.debug(c.LastName + ', ' + c.FirstName);
}

告诉我更多…

您可以筛选,重新排序和限制SOSL查询的返回结果。因为SOSL查询可以返回多个sObjects,所以这些过滤器应用在RETURNING子句中的每个sObject中。

您可以通过在对象的WHERE子句中添加条件来过滤SOSL结果。例如,这只会导致其行业服装被退回的客户:退货客户(名称,行业)行业=’服装’)

同样,通过为一个对象添加ORDER BY来支持一个sObject的排序结果。例如,这会导致返回的客户按名称字段排序:RETURNING客户(名称,行业ORDER BY名称)。

返回记录的数量可以限制为记录的一个子集。此示例将返回的客户限制为10个:RETURNING Account(Name,Industry LIMIT 10)

Salesforce Apex (基础4)SOQL

学习目标

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

  • 在Apex中编写SOQL查询。
  • 通过在开发者控制台中使用查询编辑器执行SOQL查询。
  • 通过使用匿名Apex执行嵌入在Apex中的SOQL查询。
  • 查询相关记录。

编写SOQL查询

要从Salesforce中读取记录,您需要编写一个查询。 Salesforce提供Salesforce对象查询语言(简称SOQL),您可以使用它来读取保存的记录。 SOQL类似于标准的SQL语言,但是为Force.com平台定制。
由于Apex可直接访问存储在数据库中的Salesforce记录,因此可以将SOQL查询嵌入到Apex代码中,并以简单明了的方式获取结果。当SOQL嵌入到Apex中时,它被称为内联SOQL。

要在您的Apex代码中包含SOQL查询,请将SOQL语句包装在方括号中,并将返回值分配给一个sObjects数组。例如,以下内容将检索具有两个字段(名称和电话号码)的所有客户记录,并返回一个Account sObjects数组。

Account[] accts = [SELECT Name,Phone FROM Account];

先决条件

本单位的一些查询希望组织拥有客户和联系人。在运行查询之前,请创建一些示例数据。

  1. 在开发人员控制台中,从“调试”菜单中打开“执行匿名”窗口。
  2. 在窗口中插入下面的代码片段,然后点击执行。
// 添加客户和相关联系人
Account acct = new Account(
    Name='SFDC Computing',
    Phone='(415)555-1212',
    NumberOfEmployees=50,
    BillingCity='San Francisco');
insert acct;

// 一旦插入客户,sObject将填充一个ID。
// 获取这个ID。
ID acctID = acct.ID;

// 添加一个联系人到这个客户。
Contact con = new Contact(
    FirstName='Carol',
    LastName='Ruiz',
    Phone='(415)555-1212',
    Department='Wingo',
    AccountId=acctID);
insert con;

// 添加没有联系人的客户
Account acct2 = new Account(
    Name='The SFDC Query Man',
    Phone='(310)555-1213',
    NumberOfEmployees=50,
    BillingCity='Los Angeles',
    Description='Expert in wing technologies.');
insert acct2;

使用查询编辑器

开发者控制台提供查询编辑器控制台,使您可以运行您的SOQL查询和查看结果。查询编辑器提供了一种快速检查数据库的方法。在将它们添加到您的Apex代码之前,测试您的SOQL查询是一个好方法。当您使用查询编辑器时,您只需提供SOQL语句,而不包含围绕它的Apex代码。

让我们尝试运行以下SOQL示例:

  1. 在开发者控制台中,点击 Query Editor 标签。
  2. 将以下内容复制并粘贴到查询编辑器下的第一个框中,然后单击 Execute.
SELECT Name,Phone FROM Account

您的组织中的所有客户记录都以查询结果部分显示为包含字段的行。

基本的SOQL语法

这是一个基本的SOQL查询语法:

SELECT fields FROM ObjectName [WHERE Condition]
WHERE子句是可选的。我们从一个非常简单的查询开始。例如,以下查询检索客户并为每个客户获取两个字段:ID和电话。
SELECT Name,Phone FROM Account

查询有两部分:

  1. 选择名称,电话:这部分列出您想要检索的字段。这些字段在逗号分隔列表中的SELECT关键字之后指定。或者您只能指定一个字段,在这种情况下不需要逗号(例如SELECT Phone)。
  2. FROM客户:这部分指定您要检索的标准或自定义对象。在这个例子中,它是客户。对于名为Invoice_Statement的自定义对象,它是Invoice_Statement__c。

超越基础

与其他SQL语言不同,您不能为所有字段指定*。你必须指定你想明确得到的每个字段。如果您尝试访问您在SELECT子句中未指定的字段,则会出现错误,因为该字段尚未被检索到。

您不需要在查询中指定Id字段,因为它始终在Apex查询中返回,无论是在查询中指定还是不在查询中指定。例如:SELECT Id,Phone FROM Account和SELECT Phone FROM Account是等价的语句。唯一一次你可能想要指定Id字段,如果它是唯一的字段,你正在检索,因为你必须列出至少一个字段:SELECT ID FROM Account。在查询编辑器中运行查询时,您可能还需要指定Id字段,因为除非指定,否则ID字段将不会显示。

使用条件过滤查询结果

如果您在组织中拥有多个客户,则将全部退回。如果要限制返回给满足特定条件的客户的客户,则可以在WHERE子句中添加此条件。以下示例仅检索名称为SFDC Computing的客户。请注意,字符串比较不区分大小写。

SELECT Name,Phone FROM Account WHERE Name='SFDC Computing'
WHERE子句可以包含多个使用逻辑运算符(AND,OR)和括号分组的条件。例如,此查询将返回名称为SFDC Computing的拥有超过25名员工的所有客户:
SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' AND NumberOfEmployees>25)
这是一个更复杂条件的例子。此查询将返回所有SFDC计算客户,或者拥有超过25名员工的帐单城市为洛杉矶的所有客户。
SELECT Name,Phone FROM Account WHERE (Name='SFDC Computing' OR (NumberOfEmployees>25 AND BillingCity='Los Angeles'))

超越基础

您可以使用LIKE运算符执行模糊匹配,而不是使用相等运算符(=)进行比较。例如,您可以使用以下条件检索名称以SFDC开头的所有客户:WHERE Name LIKE’SFDC%’。 %通配符匹配任何字符或不匹配。相反的_字符可以用来匹配一个字符。

订购查询结果

执行查询时,它将从Salesforce返回记录,但不会按特定顺序进行,因此每次运行查询时都不能依赖返回数组中记录的顺序。但是,您可以选择通过添加ORDER BY子句并指定记录集应该排序的字段来对返回的记录集进行排序。本示例根据“名称”字段对所有检索到的客户进行排序。

SELECT Name,Phone FROM Account ORDER BY Name
默认排序顺序按照字母顺序排列,指定为ASC。以前的声明相当于:
SELECT Name,Phone FROM Account ORDER BY Name ASC
要反转订单,请按降序使用DESC关键字:
SELECT Name,Phone FROM Account ORDER BY Name DESC

您可以对大多数字段进行排序,包括数字和文本字段。你不能在丰富的文本和多选择选项列表上排序。

在“查询编辑器”中尝试使用这些SOQL语句,并根据“名称”字段查看返回记录的顺序如何变化。

限制返回的记录数

您可以通过添加LIMIT n子句来限制返回到任意数字的记录数,其中n是要返回的记录数。当你不关心哪些记录被返回时,限制结果集很方便,但是你只想使用记录的一个子集。例如,此查询检索返回的第一个客户。请注意,使用LIMIT 1时,返回的值是一个客户,而不是数组。

Account oneAccountOnly = [SELECT Name,Phone FROM Account LIMIT 1];

把所有的东西结合在一起

您可以按照以下顺序在一个查询中组合可选子句

SELECT Name,Phone FROM Account 
                   WHERE (Name = 'SFDC Computing' AND NumberOfEmployees>25)
                   ORDER BY Name
                   LIMIT 10
在开发人员控制台中使用“执行匿名”窗口在Apex中执行以下SOQL查询。然后检查调试日志中的调试语句。应该返回一个样本客户。
Account[] accts = [SELECT Name,Phone FROM Account 
                   WHERE (Name='SFDC Computing' AND NumberOfEmployees>25)
                   ORDER BY Name
                   LIMIT 10];
System.debug(accts.size() + ' account(s) returned.');
// 写入所有客户数组信息
System.debug(accts);

在SOQL查询中访问变量

Apex中的SOQL语句可以引用Apex代码变量和表达式,前提是冒号(:)。在SOQL语句中使用局部变量称为绑定。

这个例子展示了如何在WHERE子句中使用targetDepartment变量。

String targetDepartment = 'Wingo';
Contact[] techContacts = [SELECT FirstName,LastName 
                          FROM Contact WHERE Department=:targetDepartment];

查询相关记录

Salesforce中的记录可以通过关系相互关联:查找关系或主 – 细节关系。例如,联系人与“客户”具有查找关系。当您创建或更新联系人时,您可以将其与一个客户相关联。与相同客户关联的联系人显示在客户页面的相关列表中。以同样的方式,您可以查看Salesforce用户界面中的相关记录,您可以在SOQL中查询相关记录。

要获取与父记录相关的子记录,请为子记录添加一个内部查询。内部查询的FROM子句针对关系名称而不是Salesforce对象名称。此示例包含一个内部查询以获取与每个返回的客户关联的所有联系人。 FROM子句指定联系人关系,这是客户与客户和联系人链接的默认关系。

SELECT Name, (SELECT LastName FROM Contacts) FROM Account WHERE Name = 'SFDC Computing'

下一个示例在Apex中嵌入示例SOQL查询,并演示如何使用sObject上的Contacts关系名称从SOQL结果中获取子记录。

Account[] acctsWithContacts = [SELECT Name, (SELECT FirstName,LastName FROM Contacts)
                               FROM Account 
                               WHERE Name = 'SFDC Computing'];
// 获取子记录
Contact[] cts = acctsWithContacts[0].Contacts;
System.debug('Name of first associated contact: ' 
             + cts[0].FirstName + ', ' + cts[0].LastName);
您可以使用点符号从子对象(联系人)到其父级(Account.Name)上的字段进行遍历。例如,以下Apex片段查询联系名字为Carol的记录,并且能够通过遍历客户和联系人之间的关系来检索Carol的相关客户的名称。
Contact[] cts = [SELECT Account.Name FROM Contact 
                 WHERE FirstName = 'Carol' AND LastName='Ruiz'];
Contact carol = cts[0];
String acctName = carol.Account.Name;
System.debug('Carol\'s account name is ' + acctName);

注意

本节中的示例基于标准对象。自定义对象也可以通过使用自定义关系链接在一起。自定义关系名称以__r后缀结尾。例如,发票声明通过Invoice_Statement__c自定义对象上的Line_Items__r关系链接到订单项。 (这些自定义对象是Force.com工作簿中使用的Warehouse模式的一部分。)

通过使用SOQL for循环查询批次中的记录

使用SOQL for循环,可以在for循环中包含SOQL查询。 SOQL查询的结果可以在循环内迭代。 SOQL for循环使用不同的方法来检索记录 – 通过调用SOAP API的查询和queryMore方法,使用高效分块来检索记录。通过使用SOQL for循环,可以避免碰到堆大小的限制。

SOQL for循环遍历由SOQL查询返回的所有sObject记录。 SOQL for循环的语法是:

for (variable : [soql_query]) {
    code_block
}
要么
for (variable_list : [soql_query]) {
    code_block
}

变量和variable_list必须与soql_query返回的sObjects类型相同。

最好使用SOQL for循环的sObject列表格式,因为循环为每个200 sObjects批次执行一次。这样做使您可以批量处理记录并批量执行DML操作,这有助于避免达到管理员限制。

insert new Account[]{new Account(Name = 'for loop 1'), 
                     new Account(Name = 'for loop 2'), 
                     new Account(Name = 'for loop 3')};

// sObject列表格式为每个返回的批记录执行一次for循环
Integer i=0;
Integer j=0;
for (Account[] tmp : [SELECT Id FROM Account WHERE Name LIKE 'for loop _']) {
    j = tmp.size();
    i++;
}
System.assertEquals(3, j); // 列表中应该包含三个名为“yyy”的客户
System.assertEquals(1, i); // 由于一个批次最多可以容纳200条记录,只有三条记录应该被返回,所以循环只能执行一次

Salesforce Apex (基础3)操作记录

学习目标

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

  • 使用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的一个语句。

此示例通过在一次调用中插入联系人列表来批量插入联系人。该示例然后批量更新这些联系人。

  1. 在开发者控制台中使用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;
    
  2. 检查最近在您的组织中创建的联系人。
    两位在财务部门的联系人应该有他们的头衔,财务分析师。

插入记录

如果您有一个包含新记录和现有记录混合的列表,则可以使用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。

注意

upsert调用使用该ID来匹配第一个联系人。 josh变量正被重新用于upsert调用。这个变量已经被填充了上一次插入调用的记录ID,所以在这个例子中不需要显式地设置ID。

  1. 在开发者控制台的“执行匿名”窗口中执行此代码段。
    //插入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被创建。
  2. 检查组织中的所有联系人。
    您的组织只有一个Josh Kaplan记录,而不是两个,因为upsert操作找到了现有记录并进行了更新,而不是创建新的联系人记录。凯西·布朗的一个联系记录也将在那里。或者,您可以指定一个字段用于匹配记录。此示例使用Contact上的电子邮件字段,因为它具有idLookup属性集。该示例插入Jane Smith联系人,并创建第二个Contact sObject,使用相同的电子邮件填充它,然后使用电子邮件字段进行匹配以调用upsert以更新联系人。

注意

如果在这个例子中使用了插入而不是upsert,那么将会插入一个重复的Jane Smith联系人。

  1. 在开发者控制台的“执行匿名”窗口中执行此代码段。
    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);
    
  2. 检查组织中的所有联系人。

    您的组织将只有一个Jane Smith联系更新后的描述。

删除记录

您可以使用delete语句删除持久记录。删除的记录不会从Force.com永久删除,但是它们可以从恢复位置放置在回收站中15天。

此示例显示如何删除姓氏为Smith的所有联系人。如果您已经为批量DML运行了样本,那么您的组织应该与Smith的姓氏有两个联系。在开发人员控制台中使用匿名Apex执行此代码段,然后验证是否没有与姓氏Smith的联系人。

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

注意

这个片段包含一个查询来检索联系人(一个SOQL查询)。你会在另一个单元学到更多关于SOQL的知识。

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());
}

数据库方法

Apex包含内置的Database类,它提供执行DML操作和镜像DML语句对应的方法。
这些数据库方法是静态的,并在类名称上调用。
  • Database.insert()
  • Database.update()
  • Database.upsert()
  • Database.delete()
  • Database.undelete()
  • Database.merge()

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

这是你如何调用allOrNone设置为false的插入方法。

Database.insert(recordList, false);
Database方法返回包含每条记录的成功或失败信息的结果对象。例如,insert和update操作每个都返回一个Database.SaveResult对象的数组。
Database.SaveResult[] results = Database.insert(recordList, false);

注意

Upsert返回Database.UpsertResult对象,并删除返回的Database.DeleteResult对象。

默认情况下,allOrNone参数为true,这意味着Database方法的行为与其DML语句相对应,如果遇到故障,则会引发异常。

以下两条语句相当于insert recordList;声明。

Database.insert(recordList);
和:
Database.insert(recordList, true);

超越基础

除了这些方法外,Database类还包含不作为DML语句提供的方法。例如,用于事务控制和回滚的方法,用于清空回收站以及与SOQL查询相关的方法。您将在另一个单元学习SOQL。

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

我们来看看使用数据库方法的例子。此示例基于批量DML示例,但是用Database方法替换了DML语句。用部分成功选项调用Database.insert()方法。列表中的一个联系人没有任何有意使用的字段,并且会导致错误,因为如果没有所需的姓氏字段,则无法保存联系人。三个联系人被提交,没有任何字段的联系人会产生错误。本示例的最后一部分遍历返回的结果并将调试消息写入调试日志。

  1. 在开发者控制台的“执行匿名”窗口中执行此示例。
    // 创建联系人列表
    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());
    	 }
        }
    }
    
  2. 验证调试消息(使用过滤器的DEBUG关键字)。
    应该报告一个故障,应该插入三个联系人。

你应该使用DML语句还是数据库方法?

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