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语句。

Salesforce Apex (基础2)sObjects

学习目标

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

  • 描述sObjects和Salesforce记录之间的关系。
  • 创建并使用特定的sObject变量。
  • 将一个通用的sObject转换为一个特定的sObject。

使用sObjects

由于Apex与数据库紧密集成,因此您可以直接从Apex访问Salesforce记录及其字段。 Salesforce中的每个记录都本地表示为Apex中的sObject。例如,Acme客户记录对应于Apex中的Account sObject。您可以在用户界面中查看和修改的Acme记录的字段也可以在sObject上直接读取和修改。
下表列出了Acme客户示例记录的一些填充字段。 Account sObject是客户记录的抽象,并将客户字段信息作为对象保存在内存中。
表1.检索到的记录的AccountSObject
客户字段
Id 001D000000JlfXe
公司名称 Acme
电话 (415)555-1212
员工人数 100

每个Salesforce记录在插入Salesforce之前都被表示为一个sObject。同样,从Salesforce检索持久性记录时,它们存储在sObject变量中。

Salesforce中的标准和自定义对象记录将映射到Apex中的sObject类型。以下是Apex中用于标准对象的一些常用的sObject类型名称。

  • 客户
  • 联系
  • 潜在客户
  • 机会

如果您在组织中添加了自定义对象,请使用Apex中自定义对象的API名称。例如,名为Merchandise的自定义对象对应于Apex中的Merchandise__c sObject。

创建sObject变量

要创建一个sObject,你需要声明一个变量并将其分配给一个sObject实例。变量的数据类型是sObject类型。

以下示例创建一个类型为Account的sObject变量,并将其分配给名称为Acme的新客户。

Account acct = new Account(Name='Acme');

sObject和字段名称

sObjects的名称对应于相应的标准或自定义对象的API名称。同样,sObject字段的名称对应于相应字段的API名称。

对象和字段的API名称可能与标签不同。例如,“雇员”字段具有“雇员”标签,并在“客户记录”页面上显示为“雇员”,但其API名称为“NumberOfEmployees”。要在Apex中访问此字段,您需要使用该字段的API名称:NumberOfEmployees。

以下是用于自定义对象和自定义字段的API名称的一些规则的亮点。

对于自定义对象和自定义字段,API名称始终以__c后缀结尾。对于自定义关系字段,API名称以__r后缀结尾。例如:

  • 具有Merchandise 标签的自定义对象具有Merchandise__c的API名称。
  • 描述标签的自定义字段的API名称为Description__c。
  • 具有Items标签的自定义关系字段具有Items__r的API名称。

另外,标签中的空格在API名称中用下划线代替。例如,Employee Seniority的自定义字段名称具有Employee_Seniority__c的API名称。

查找对象和字段名称

要查找在Apex中使用的标准对象及其字段的名称,请参阅Salesforce和Force.com的对象参考。

对于自定义对象,请在您的组织中查找对象和字段API名称。从“设置”中,在“快速查找”框中输入对象,然后选择“对象”,然后单击对象的名称。

创建sObjects和添加字段

在插入Salesforce记录之前,必须首先在内存中创建它作为sObject。与其他任何对象一样,sObject使用新的运算符创建:

Account acct = new Account();

API对象名称成为Apex中sObject变量的数据类型。在这个例子中,Account是acct变量的数据类型。

由acct变量引用的客户是空的,因为我们还没有填充它的任何字段。有两种方法可以添加字段:通过构造函数或使用点符号。

添加字段的最快方法是在构造函数中将它们指定为名称 – 值对。例如,这个语句创建一个新的客户sObject,并用字符串值填充它的Name字段。

Account acct = new Account(Name='Acme');
名称字段是客户唯一必填字段,这意味着必须在插入新记录之前填充该字段。但是,您也可以填充其他字段以及新记录。这个例子还增加了一个电话号码和雇员的数量。
Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);
或者,您可以使用点符号将字段添加到sObject。以下示例与前面的示例相同,但需要多行代码。
Account acct = new Account();
acct.Name = 'Acme';
acct.Phone = '(415)555-1212';
acct.NumberOfEmployees = 100;

使用通用的sObject数据类型

通常,在使用sObjects时,使用特定的sObject数据类型(例如标准对象的Account或Book__c)作为名为Book的自定义对象。但是,当您不知道您的方法正在处理的sObject类型时,可以使用通用的sObject数据类型。
使用通用sObject数据类型声明的变量可以引用任何Salesforce记录,无论它是标准还是自定义对象记录。
A generic sObject variable can point to any Salesforce record

此示例显示如何将通用的sObject变量分配给任何Salesforce对象:客户和名为Book__c的自定义对象。

sObject sobj1 = new Account(Name='Trailhead');
sObject sobj2 = new Book__c(Name='Workbook 1');
相比之下,使用特定的sObject数据类型声明的变量只能引用相同类型的Salesforce记录。
A specific sObject variable can point to the Salesforce record of the same type only

将泛型对象转换为特定的对象类型

当你处理通用的sObjects时,你有时需要将你的sObject变量转换为特定的sObject类型。这样做的好处之一就是能够使用点符号访问字段,这在通用的sObject上是不可用的。由于sObject是所有特定sObject类型的父类型,因此可以将通用sObject转换为特定的sObject。这个例子展示了如何将一个通用的sObject转换为Account。

// 将一个通用的sObject转换为一个Account
Account acct = (Account)myGenericSObject;
// 现在,您可以使用点符号来访问客户上的字段Account
String name = acct.Name;
String phone = acct.Phone;

告诉我更多…

与特定的sObjects类型不同,通用sObjects只能通过newSObject()方法创建。而且,只能通过put()和get()方法访问通用sObject的字段。

在这个单元中,你已经学习了什么sObjects以及如何使用它们。但是,创建一个sObject并不会将其作为记录保存在数据库中。要将sObject保存为记录,并用它来做其他事情,请使用数据操作语言(DML)。要检索记录,请使用Salesforce对象查询语言(SOQL)。查看以后的单元,了解DML和SOQL。

Salesforce Apex (基础1)入门

学习目标

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

  • 描述Apex编程语言的主要特点。
  • 保存Apex类并使用Anonymous.Apex调用方法。
  • 使用开发者控制台检查调试日志。

Apex入门

Apex是一种使用类似Java的语法的编程语言,像数据库存储过程一样行事。 Apex使开发人员能够将业务逻辑添加到系统事件中,如按钮点击,相关记录更新和Visualforce页面。

作为一种语言,Apex是:

  • Hosted-Apex在服务器(Force.com平台)上保存,编译并执行。
  • 面向对象-Apex支持类,接口和继承。
  • 强类型-Apex在编译时验证对对象的引用。
  • 多租户感知 – 因为Apex运行在多租户平台上,所以它通过强制实施限制防止代码独占共享资源,从而防止代码走失。
  • 与数据库集成 – 访问和操作记录非常简单。 Apex提供对记录及其字段的直接访问,并提供语句和查询语言来操作这些记录。
  • 关注数据–Apex提供对数据库的事务访问,允许您回滚操作。
  • 易于使用-Apex基于熟悉的Java成语。
  • 易于测试 – Apex为单元测试创​​建,执行和代码覆盖提供了内置的支持。
  • Salesforce确保所有定制的Apex代码在任何平台升级之前执行所有单元测试,并按预期工作。
  • Versioned-Custom Apex代码可以针对不同版本的API进行保存。
Apex is a cloud-based programming language

Apex 语言亮点

像其他面向对象的编程语言一样,这些是Apex支持的一些语言结构:

  • 类,接口,属性和集合(包括数组)。
  • 对象和数组表示法。
  • 表达式,变量和常量。
  • 条件语句(if-then-else)和控制流语句(for循环和while循环)。

与其他面向对象的编程语言不同,Apex支持:

  • 作为Apex的云开发是在云中存储,编译和执行的。
  • 触发器,类似于数据库系统中的触发器。
  • 数据库语句,允许您直接进行数据库调用和查询语言来查询和搜索数据。
  • 事务和回滚。
  • 全局访问修饰符,它比public修饰符更宽松,并允许跨命名空间和应用程序访问。
  • 自定义代码的版本。

另外,Apex是一个不区分大小写的语言。

开发工具

您可以使用Salesforce用户界面直接在浏览器中编写Apex并访问调试信息。打开您的名字下的开发者控制台或快速访问菜单(设置齿轮图标)。

您也可以使用Eclipse的Force.com IDE插件在客户端上编写Apex。请参阅Salesforce Developer网站上的Force.com IDE。

数据类型概述

Apex支持各种数据类型,包括特定于Salesforce的数据类型(sObject数据类型)。
Apex支持以下数据类型。

  • 诸如Integer,Double,Long,Date,Datetime,String,ID,Boolean等原语。
  • 一个sObject,或者作为一个通用的sObject或者一个特定的sObject,比如一个Account,Contact或者MyCustomObject__c(你将在后面的单元中学到更多关于sObjects的知识)。
  • 收藏品包括:
    • 基元,sObjects,用户定义的对象,从Apex类创建的对象或集合的列表(或数组)
    • 一组原语
    • 从基元到基元,sObject或集合的映射
  • 一个键入的值列表,也被称为枚举
  • 用户定义的Apex类
  • 系统提供的Apex类

Apex收藏:列表

列表包含对象的有序集合。 Apex中的列表与数组同义,两者可以互换使用。

以下两个声明是等价的。 colors变量是使用List语法声明的。

List<String> colors = new List<String>();
另外,颜色变量可以声明为一个数组,但分配给一个列表而不是一个数组。
String[] colors = new List<String>();

通常,创建列表而不是数组会更容易,因为列表不需要提前确定需要分配多少个元素。

您可以在创建列表时将元素添加到列表中,或者通过调用add()方法创建列表后添加元素。这第一个例子显示了向元素添加元素的两种方法。

// 创建一个列表并向其中添加元素
List<String> colors = new List<String> { 'red', 'green', 'blue' };

// 创建元素后,将元素添加到列表中
List<String> moreColors = new List<String>();
moreColors.add('orange');
moreColors.add('purple');
列表元素可以通过指定方括号之间的索引来读取,就像数组元素一样。另外,您可以使用get()方法读取列表元素。这个例子是基于前面例子中创建的列表,并展示了如何使用任一方法读取列表元素。该示例还显示了如何迭代数组元素。
// 从列表中获取元素
String color1 = moreColors.get(0);
String color2 = moreColors[0];
System.assertEquals(color1, color2);

// 遍历列表来读取元素
for(Integer i=0;i<colors.size();i++) {
    // Write value to the debug log
    System.debug(colors[i]);
}

超越基础

Apex支持另外两种集合类型:Set和Map。您可以在“Apex开发人员指南”的“集合”部分了解更多。

Apex 类

Apex类的好处之一是代码重用。类方法可以被触发器和其他类调用。以下教程将引导您保存组织中的示例类,使用此类发送电子邮件以及检查调试日志。

保存一个Apex类

将EmailManager类保存在您的组织中:

  1. 打开您的名字下的开发者控制台或快速访问菜单(设置齿轮图标Setup gear icon)。
  2. 在开发者控制台中,点击 File | New | Apex Class ,然后输入EmailManager作为类名称,然后单击OK。
  3. 用EmailManager类的例子替换默认的类体。

    EmailManager类具有公共方法(sendMail()),用于发送电子邮件并使用Apex类库的内置消息处理方法。另外,这个类有一个私有的帮助方法(inspectResults()),它不能被外部调用,因为它是私有的,但只在类中使用。这个帮助方法检查电子邮件发送调用的结果,并由sendMail()调用。

    public class EmailManager {
    
        // 公共方法
        public void sendMail(String address, String subject, String body) {
            // 创建一个电子邮件对象
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {address};
            mail.setToAddresses(toAddresses);
            mail.setSubject(subject);
            mail.setPlainTextBody(body);
            // 将此电子邮件传递给内置的sendEmail方法
            // 消息类的
            Messaging.SendEmailResult[] results = Messaging.sendEmail(
                                     new Messaging.SingleEmailMessage[] { mail });
            
            // 调用一个帮助器方法来检查返回的结果
            inspectResults(results);
        }
        
        // 辅助方法
        private static Boolean inspectResults(Messaging.SendEmailResult[] results) {
            Boolean sendResult = true;
            
            // sendEmail返回一个结果对象数组。
            // 遍历列表来检查结果。
            // 在这个类中,方法只发送一封邮件,
            // 所以我们应该只有一个结果。
            for (Messaging.SendEmailResult res : results) {
                if (res.isSuccess()) {
                    System.debug('Email sent successfully');
                }
                else {
                    sendResult = false;
                    System.debug('发生以下错误:' + res.getErrors());                 
                }
            }
            
            return sendResult;
        }
    
    }
  4. 点击Ctrl + S保存你的课程。

    超越基础

    刚刚保存的类使用面向对象编程(OOP)。该类封装了与管理电子邮件有关的方法。为了成为OOP的一个完美的例子,类还将包含成员变量(属性)和访问器方法来访问这些属性,但为了简单起见,我们的类没有这些。

    Salesforce在您保存时编译您的类。

调用方法发送电子邮件

我们来调用公共方法。我们将使用匿名的Apex执行来执行此操作。匿名Apex允许您即时运行代码行,并且是调用Apex的一种便捷方式,尤其是测试功能。与任何其他Apex执行一样,也会生成调试日志结果。

注意

还有其他方法可以通过触发器来调用Apex。您将在另一个模块中了解有关触发器的更多信息。

  1. 在开发者控制台中,点击 Debug | Open Execute Anonymous Window.
  2. 在打开的窗口中,输入以下内容。用您的电子邮件地址替换“您的电子邮件地址”。
    EmailManager em = new EmailManager();
    em.sendMail('Your email address', 'Trailhead Tutorial', '123 body');
    
  3. 点击 Execute.

    现在这个方法已经执行了,你应该在收件箱里收到一封电子邮件。查看你的电子邮件!

检查调试日志

调试日志对于调试代码很有用。当Apex方法执行时,调用会记录在调试日志中。而且,您可以将自己的调试消息写入日志,这有助于在出现错误时调试代码。由sendMail()调用的inspectResults()辅助方法使用System.debug()方法将消息写入日志,以指示电子邮件发送操作是成功还是有错误。您可以在执行该方法时生成的调试日志中查找这些消息。

  1. 在开发者控制台中,点击日志标签,然后双击列表中最近的日志。
  2. 选择 Debug Only 来过滤日志,以便仅显示System.debug()语句的日志行。
    Filter the debug log in the Developer Console to view debug messages

    注意

    另外,您可以通过在“筛选”字段中搜索任何关键字来筛选调试日志,或者通过选择任何其他选项来筛选调试日志。有关更多信息,请参阅日志检查器帮助。

    在过滤的日志视图中,您会看到以下消息,假设电子邮件发送没有错误。

    DEBUG|Email sent successfully

调用静态方法

因为我们类中的sendMail()方法不能访问类成员变量,所以它不需要是一个实例方法。让我们通过添加静态关键字到它的声明来将其更改为静态方法。静态方法比实例方法更容易调用,因为它们不需要在类的实例上调用,而是直接在类名上调用。

  1. 在开发人员控制台中,找到EmailManager类的打开选项卡,并将sendMail()方法定义的第一行修改为以下内容(唯一的变化是添加的static关键字)。
    public static void sendMail(String address, String subject, String body) {
  2. 按Ctrl + S保存该类。
  3. 修改“执行匿名”窗口中的语句以调用类名称上的静态方法。
    EmailManager.sendMail('Your email address', 'Trailhead Tutorial', '123 body');
    
  4. 点击 Execute.

    现在这个方法已经执行了,你可以像在前面的步骤一样检查你的电子邮件,以及可选的调试日志。