目录

Apex - 快速指南

Apex - Overview

什么是Apex?

Apex是Salesforce.com开发的专有语言。 根据官方定义,Apex是一种强类型,面向对象的编程语言,允许开发人员在Force.com平台服务器上执行流和事务控制语句,同时调用Force.com API。

它具有类似Java的语法,就像数据库存储过程一样。 它使开发人员能够为大多数系统事件添加业务逻辑,包括按钮单击,相关记录更新和Visualforce pages.Apex代码可以由Web服务请求和对象上的触发器启动。 Apex包含在Performance Edition,Unlimited Edition,Enterprise Edition和Developer Edition中。

Apex代码执行场景

Apex作为一种语言的特点

现在让我们讨论Apex作为语言的特征 -

集成 (Integrated)

Apex内置了对INSERT,UPDATE,DELETE等DML操作的支持以及DML异常处理。 它支持内联SOQL和SOSL查询处理,它返回一组sObject记录。 我们将在以后的章节中详细研究sObject,SOQL,SOSL。

Java喜欢语法并且易于使用

Apex易于使用,因为它使用Java之类的语法。 例如,变量声明,循环语法和条件语句。

与数据紧密结合

Apex专注于数据,旨在将多个查询和DML语句一起执行。 它在Database上发出多个事务语句。

强类型

Apex是一种强类型语言。 它使用对sObject等模式对象的直接引用,如果删除任何无效引用或者数据类型错误,则会快速失败。

多租户环境

Apex在多租户环境中运行。 因此,Apex运行时引擎旨在密切防范失控代码,防止它独占共享资源。 任何违反限制的代码都会因易于理解的错误消息而失败。

自动升级

Apex作为Salesforce版本的一部分进行了升级。 我们不必手动升级它。

易于测试

Apex为单元测试的创建和执行提供内置支持,包括指示覆盖了多少代码的测试结果,以及代码的哪些部分可以更高效。

开发人员何时应该选择Apex?

当我们无法使用预先构建的和现有的开箱即用功能来实现复杂的业务功能时,应该使用Apex。 以下是我们需要使用apex而不是Salesforce配置的情况。

Apex应用程序

我们可以在需要时使用Apex -

  • 通过集成其他系统来创建Web服务。

  • 为电子邮件或电子邮件设置创建电子邮件服务

  • 同时对多个对象执行复杂验证以及自定义验证实现。

  • 创建现有工作流功能或流不支持的复杂业务流程。

  • 创建自定义事务逻辑(在整个事务中发生的逻辑,而不仅仅是单个记录或对象),比如使用Database方法更新记录。

  • 修改记录时执行某些逻辑,或者在某些事件导致触发器触发时修改相关对象的记录。

Apex的工作结构

如下图所示(参考:Salesforce Developer Documentation),Apex完全按需运行Force.com平台

Apex代码的Apex编译

行动的流程

当开发人员保存代码时以及当最终用户执行调用Apex代码的某个操作时,有两个操作序列,如下所示 -

开发者行动

当开发人员将Apex代码写入并保存到平台时,平台应用程序服务器首先将代码编译为Apex运行时解释器可以理解的一组指令,然后将这些指令保存为元数据。

最终用户行动

当最终用户通过单击按钮或访问Visualforce页面触发Apex的执行时,平台应用程序服务器从元数据中检索已编译的指令,并在返回结果之前通过运行时解释程序发送它们。 与标准应用程序平台请求相比,最终用户观察到执行时间没有差异。

由于Apex是Salesforce.com的专有语言,因此它不支持通用编程语言所具有的某些功能。 以下是Apex不支持的一些功能 -

  • 它无法显示用户界面中的元素。

  • 您无法更改标准SFDC提供的功能,也无法阻止标准功能执行。

  • 您无法更改标准SFDC提供的功能,也无法阻止标准功能执行。

  • 创建多个线程也是不可能的,因为我们可以用其他语言来创建它。

了解Apex语法

Apex代码通常包含许多我们可能熟悉的其他编程语言。

变量声明

作为强类型语言,您必须在Apex中声明具有数据类型的每个变量。 如下面的代码(下面的屏幕截图)所示,lstAcc被声明为数据类型为List of Accounts。

SOQL查询

这将用于从Salesforce数据库中获取数据。 下面的屏幕截图中显示的查询是从Account对象获取数据。

Loop 语句

此循环语句用于迭代列表或迭代一段代码指定的次数。 在下面的屏幕截图中显示的代码中,迭代将与我们拥有的记录数相同。

Flow Control 语句

If语句用于此代码中的流控制。 基于某些条件,决定是执行还是停止执行特定代码。 例如,在下面显示的代码中,它检查列表是否为空或包含记录。

DML 语句

对数据库中的记录执行记录插入,更新,升级,删除操作。 例如,下面给出的代码有助于使用新字段值更新帐户。

以下是Apex代码段的外观示例。 我们将在本教程中进一步研究所有这些Apex编程概念。

Apex示例代码语法

Apex - Environment

在本章中,我们将了解Salesforce Apex开发的环境。 假设您已经为进行Apex开发设置了Salesforce版本。

您可以在Sandbox或Salesforce的Developer Edition中开发Apex代码。 沙盒组织是您组织的副本,您可以在其中编写代码并对其进行测试,而不会冒数据修改或干扰正常功能的风险。 根据标准工业实践,您必须在Sandbox中开发代码,然后将其部署到生产环境中。

在本教程中,我们将使用Salesforce的Developer Edition。 在Developer Edition中,您将无法选择创建Sandbox组织。 Sandbox功能在其他版本的Salesforce中可用。

部署过程

Apex代码开发工具

在所有版本中,我们可以使用以下三种工具中的任何一种来开发代码 -

  • Force.com开发者控制台
  • Force.com IDE
  • Salesforce用户界面中的代码编辑器

Note −我们将在整个教程中使用Developer Console进行代码执行,因为它简单易用且易于学习。

Force.com开发者控制台

Developer Console是一个集成开发环境,其中包含一组工具,可用于在Salesforce组织中创建,调试和测试应用程序。

请按照以下步骤打开开发者控制台 -

Step 1 −转到名称→开发者控制台

打开开发者控制台Step1

Step 2 −单击“开发人员控制台”,将出现一个窗口,如下面的屏幕截图所示。

打开开发者控制台Step_2

以下是可以使用Developer Console执行的一些操作。

  • Writing and compiling code −您可以使用源代码编辑器编写代码。 保存触发器或类时,代码会自动编译。 将报告任何编译错误。

  • Debugging −您可以使用源代码编辑器编写代码。 保存触发器或类时,代码会自动编译。 将报告任何编译错误。

  • Testing −您可以查看调试日志并设置有助于调试的检查点。

  • Checking performance −您可以执行组织中特定测试类或所有类的测试,并可以查看测试结果。 此外,您可以检查代码覆盖率。

  • SOQL queries −您可以检查调试日志以找到性能瓶颈。

  • Color coding and autocomplete −源代码编辑器使用颜色方案以便于代码元素的可读性,并为类和方法名称提供自动完成功能。

在Developer Console中执行代码

本教程中提到的所有代码片段都需要在开发人员控制台中执行。 请按照以下步骤在Developer Console中执行步骤。

Step 1 - 使用login.salesforce.com登录Salesforce.com。 复制教程中提到的代码片段。 现在,我们将使用以下示例代码。

String myString = 'MyString';
System.debug('Value of String Variable'+myString);
登录屏幕

Step 2 - 要打开Developer Console,请单击Name→Developer Console,然后单击Execute Anonymous,如下所示。

在Developer Console中执行代码Step1

在Developer Console中执行代码step2

Step 3 - 在此步骤中,将出现一个窗口,您可以将代码粘贴到那里。

在开发者控制台步骤3中执行代码

Step 4 - 当我们单击Execute ,将打开调试日志。 一旦日志出现在窗口中,如下所示,然后单击日志记录。

记录日志

然后在窗口中键入“USER”,如下所示,输出语句将出现在调试窗口中。 此“USER”语句用于过滤输出。

在Developer Console中执行代码Step4

基本上,您将遵循上述所有步骤来执行本教程中的任何代码段。

Apex - Example

企业应用开发示例

对于我们的教程,我们将实施化学设备和加工公司的CRM应用程序。 该公司涉及供应商并提供服务。 我们将在整个教程中编写与此示例相关的小代码片段,以详细了解每个概念。

要执行本教程中的代码,您需要创建两个对象:Customer和Invoice对象。 如果您已经知道如何在Salesforce中创建这些对象,则可以跳过下面给出的步骤。 否则,您可以按照下面的分步指南进行操作。

创建客户对象

我们将首先设置Customer对象。

Step 1 - 转到“设置”,然后搜索“对象”,如下所示。 然后单击Objects链接,如下所示。

客户对象Ceation Step1

客户对象Ceation Step1-2

Step 2 - 打开对象页面后,单击“ Create New Object ”按钮,如下所示。

客户对象Ceation Step3

Step 3 - 单击按钮后,将出现新的对象创建页面,然后输入下面输入的所有对象详细信息。 对象名称应为Customer。 您只需在字段中输入信息,如下面的屏幕截图所示,并保留其他默认设置。

客户对象Ceation Step4

输入信息,然后单击“保存”按钮 -

客户对象Ceation Step5

通过执行上述步骤,我们已成功创建Customer对象。

为Customer对象创建自定义字段

现在我们已经设置了Customer对象,我们将创建一个“Active”字段,然后您可以按照类似的步骤创建其他字段。 该字段的名称和API名称将在屏幕截图中给出。

Step 1 - 我们将创建一个名为“Active”的数据类型为Checkbox的字段。 转到“设置”并单击它。

客户自定义字段创建Step1

Step 2 - 搜索“对象”,如下所示,然后单击它。

客户自定义字段创建Step2

Step 3 - 单击对象“客户”。

客户自定义字段创建步骤3

Step 4 - 单击Customer对象链接并显示对象详细信息页面后,单击New按钮。

客户自定义字段创建Step4

Step 5 - 现在,选择数据类型为Checkbox,然后单击Next。

客户自定义字段创建Step5

Step 6 - 输入字段名称和标签,如下所示。

客户自定义字段创建步骤6

Step 7 - 单击Visible,然后单击Next。

客户自定义字段创建Step7

Step 8 - 现在点击“保存”。

客户自定义字段创建步骤8

按照上述步骤,我们会创建自定义字段“活动”。 您必须为其余字段执行上述所有自定义字段创建步骤。 创建所有字段后,这是客户对象的最终视图 -

最终查看客户对象

创建发票对象

Step 1 - 转到“设置”并搜索“对象”,然后单击“对象”链接,如下所示。

发票对象创建Step1

发票对象创建步骤2

Step 2 - 打开对象页面后,单击“创建新对象”按钮,如下所示。

发票对象创建Step3

Step 3 - 单击按钮后,将出现新的对象创建页面,如下面的屏幕截图所示。 您需要在此处输入详细信息。 对象名称应为Invoice。 这与我们在本教程前面创建Customer对象的方式类似。

发票对象创建Step4

Step 4 - 输入如下所示的信息,然后单击“保存”按钮。

发票对象创建步骤5

通过执行这些步骤,将创建您的Invoice对象。

为Invoice对象创建自定义字段

我们将在Invoice对象上创建字段描述,如下所示 -

Step 1 - 转到“设置”并单击它。

客户对象创建Step1

Step 2 - 搜索“对象”,如下所示,然后单击它。

客户对象创建步骤1-2

Step 3 - 点击对象'发票'。

发票自定义字段创建Step3

然后单击“新建”。

发票自定义字段创建步骤

Step 4 - 选择数据类型为文本区域,然后单击下一步按钮。

发票自定义字段创建Step5

Step 5 - 输入以下信息。

发票自定义字段创建步骤6

Step 6 - 单击Visible,然后单击Next。

发票自定义字段创建Step7

Step 7 - 单击“保存”。

发票自定义字段创建步骤8

同样,您可以在Invoice对象上创建其他字段。

最终发票对象视图

通过这个,我们创建了本教程所需的对象。 我们将在后续章节中基于这些对象学习各种示例。

Apex - Data Types

了解数据类型

Apex语言是强类型的,因此Apex中的每个变量都将使用特定的数据类型声明。 所有顶点变量最初初始化为null。 始终建议开发人员确保为变量分配正确的值。 否则,这些变量在使用时会抛出空指针异常或任何未处理的异常。

Apex支持以下数据类型 -

  • 基本类型(Primitive)(Integer, Double, Long, Date, Datetime, String, ID, or Boolean)

  • Collections(Lists, Sets and Maps)(将在第6章中介绍)

  • sObject

  • Enums

  • Classes, Objects and Interfaces(将在第11,12和13章中介绍)

在本章中,我们将查看所有原始数据类型,sObject和枚举。 我们将在接下来的章节中查看集合,Classes, Objects and Interfaces,因为它们是单独学习的关键主题。

原始数据类型

在本节中,我们将讨论Apex支持的原始数据类型。

Integer

32位数字,不包含任何小数点。 此值的范围从-2,147,483,648开始,最大值最大为2,147,483,647。

Example

我们想要申报一个变量,该变量将存储需要运送给化学加工厂买方的桶数量。

Integer barrelNumbers = 1000;
system.debug(' value of barrelNumbers variable: '+barrelNumbers);

System.debug()函数打印变量的值,以便我们可以使用它来调试或了解变量当前保持的值。

将以上代码粘贴到Developer控制台,然后单击Execute。 生成日志后,它会将变量“barrelNumbers”的值显示为1000。

Boolean

此变量可以是true,false或null。 很多时候,这种类型的变量可以用作编程中的标志,以识别是否设置了特定条件。

Example

如果要将Boolean shipmentDispatched设置为true,则可以将其声明为 -

Boolean shipmentDispatched;
shipmentDispatched = true;
System.debug('Value of shipmentDispatched '+shipmentDispatched);

Date

此变量类型表示日期。 这只能存储日期而不是时间。 为了保存日期和时间,我们需要将它存储在DateTime的变量中。

Example

请考虑以下示例以了解Date变量的工作原理。

//ShipmentDate can be stored when shipment is dispatched.
Date ShipmentDate = date.today();
System.debug('ShipmentDate '+ShipmentDate);

Long

这是一个没有小数点的64位数字。 当我们需要一系列比Integer提供的值更宽的值时,可以使用此方法。

Example

如果要存储公司收入,那么我们将使用数据类型为Long。

Long companyRevenue = 21474838973344648L;
system.debug('companyRevenue'+companyRevenue);

Object

我们可以将其称为Apex支持的任何数据类型。 例如,Class变量可以是该类的对象,而sObject泛型类型也是一个对象,类似于Account的特定对象类型也是一个Object。

Example

请考虑以下示例以了解bject变量的工作原理。

Account objAccount = new Account (Name = 'Test Chemical');
system.debug('Account value'+objAccount);

Note - 您也可以创建预定义类的对象,如下所示 -

//Class Name: MyApexClass
MyApexClass classObj = new MyApexClass();

这是将用作类变量的类对象。

String

字符串是单引号内的任何字符集。 它对字符数没有任何限制。 这里,堆大小将用于确定字符数。 这限制了Apex计划对资源的垄断,也确保了它不会变得太大。

Example

String companyName = 'Abc International';
System.debug('Value companyName variable'+companyName);

Time

此变量用于存储特定时间。 应始终使用系统静态方法声明此变量。

Blob

Blob是二进制数据的集合,存储为对象。 当我们想要将salesforce中的附件存储到变量中时,将使用此方法。 此数据类型将附件转换为单个对象。 如果要将blob转换为字符串,那么我们可以使用toString和valueOf方法。

sObject

这是Salesforce中的特殊数据类型。 它类似于SQL中的表,包含与SQL中的列类似的字段。 有两种类型的sObjects - 标准和自定义。

例如,Account是标准sObject,任何其他用户定义的对象(如我们创建的Customer对象)都是Custom sObject。

Example

//Declaring an sObject variable of type Account
Account objAccount = new Account();
//Assignment of values to fields of sObjects
objAccount.Name = 'ABC Customer';
objAccount.Description = 'Test Account';
System.debug('objAccount variable value'+objAccount);
//Declaring an sObject for custom object APEX_Invoice_c
APEX_Customer_c objCustomer = new APEX_Customer_c();
//Assigning value to fields
objCustomer.APEX_Customer_Decscription_c = 'Test Customer';
System.debug('value objCustomer'+objCustomer);

Enum

枚举是一种抽象数据类型,它存储一组有限的指定标识符的一个值。 您可以使用关键字Enum来定义枚举。 枚举可以用作Salesforce中的任何其他数据类型。

Example

您可以通过执行以下代码声明化合物的可能名称 -

//Declaring enum for Chemical Compounds
public enum Compounds {HCL, H2SO4, NACL, HG}
Compounds objC = Compounds.HCL;
System.debug('objC value: '+objC);

Apex - Variables

Java和Apex在很多方面都很相似。 Java和Apex中的变量声明也完全相同。 我们将讨论一些示例以了解如何声明局部变量。

String productName = 'HCL';
Integer i = 0;
Set<string> setOfProducts = new Set<string>();
Map<id, string> mapOfProductIdToName = new Map<id, string>();

请注意,所有变量都赋值为null。

Declaring Variables

您可以在Apex中声明变量,如String和Integer,如下所示 -

String strName = 'My String';  //String variable declaration
Integer myInteger = 1;         //Integer variable declaration
Boolean mtBoolean = true;      //Boolean variable declaration

Apex variables are Case-Insensitive

这意味着下面给出的代码将抛出一个错误,因为变量'm'已被声明两次,并且两者都将被视为相同。

Integer m = 100;
for (Integer i = 0; i<10; i++) {
   integer m = 1; //This statement will throw an error as m is being declared
   again
   System.debug('This code will throw error');
}

Scope of Variables

Apex变量从代码中声明的位置开始生效。 因此不允许在代码块中再次重新定义相同的变量。 此外,如果在方法中声明任何变量,则该变量范围将仅限于该特定方法。 但是,可以在整个类中访问类变量。

Example

//Declare variable Products
List<string> Products = new List<strings>();
Products.add('HCL');
//You cannot declare this variable in this code clock or sub code block again
//If you do so then it will throw the error as the previous variable in scope
//Below statement will throw error if declared in same code block
List<string> Products = new List<strings>();

Apex - Strings

与任何其他编程语言一样,Apex中的字符串是任何没有字符限制的字符集。

Example

String companyName = 'Abc International';
System.debug('Value companyName variable'+companyName);

字符串的方法 (String Methods)

Salesforce中的String类有许多方法。 我们将在本章中介绍一些最重要和最常用的字符串方法。

contains

如果给定的字符串包含提到的子字符串,则此方法将返回true。

Syntax

public Boolean contains(String substring)

Example

String myProductName1 = 'HCL';
String myProductName2 = 'NAHCL';
Boolean result = myProductName2.contains(myProductName1);
System.debug('O/p will be true as it contains the String and Output is:'+result);

equals

如果给定字符串和方法中传递的字符串具有相同的二进制字符序列且它们不为null,则此方法将返回true。 您也可以使用此方法比较SFDC记录ID。 此方法区分大小写。

Syntax

public Boolean equals(Object string)

Example

String myString1 = 'MyString';
String myString2 = 'MyString';
Boolean result = myString2.equals(myString1);
System.debug('Value of Result will be true as they are same and Result is:'+result);

equalsIgnoreCase

如果stringtoCompare与给定字符串具有相同的字符序列,则此方法将返回true。 但是,此方法不区分大小写。

Syntax

public Boolean equalsIgnoreCase(String stringtoCompare)

Example

以下代码将返回true,因为字符串字符和序列相同,忽略区分大小写。

String myString1 = 'MySTRING';
String myString2 = 'MyString';
Boolean result = myString2.equalsIgnoreCase(myString1);
System.debug('Value of Result will be true as they are same and Result is:'+result);

remove

此方法从给定字符串中删除stringToRemove中提供的字符串。 当您想要从字符串中删除某些特定字符并且不知道要删除的字符的确切索引时,这非常有用。 此方法区分大小写,如果出现相同的字符序列但情况不同,则无法使用。

Syntax

public String remove(String stringToRemove)

Example

String myString1 = 'This Is MyString Example';
String stringToRemove = 'MyString';
String result = myString1.remove(stringToRemove);
System.debug('Value of Result will be 'This Is Example' as we have removed the MyString 
   and Result is :'+result);

removeEndIgnoreCase

此方法从给定字符串中删除stringToRemove中提供的字符串,但仅限于它在结尾处出现。 此方法不区分大小写。

Syntax

public String removeEndIgnoreCase(String stringToRemove)

Example

String myString1 = 'This Is MyString EXAMPLE';
String stringToRemove = 'Example';
String result = myString1.removeEndIgnoreCase(stringToRemove);
System.debug('Value of Result will be 'This Is MyString' as we have removed the 'Example'
   and Result is :'+result);

startsWith

如果给定的字符串以方法中提供的前缀开头,则此方法将返回true。

Syntax

public Boolean startsWith(String prefix)

Example

String myString1 = 'This Is MyString EXAMPLE';
String prefix = 'This';
Boolean result = myString1.startsWith(prefix);
System.debug(' This will return true as our String starts with string 'This' and the 
   Result is :'+result);

Apex - Arrays

Apex中的数组与Apex中的数组基本相同。 Arrays和Lists之间没有逻辑上的区别,因为它们的内部数据结构和方法也相同,但是数组语法很像Java一样传统。

以下是产品阵列的表示 -

Index 0 - HCL

Index 1 - H2SO4

Index 2 - NACL

Index 3 - H2O

Index 4 - N2

Index 5 - U296

语法 (Syntax)

<String> [] arrayOfProducts = new List<String>();

例子 (Example)

假设,我们必须存储我们产品的名称 - 我们可以使用Array在哪里,我们将存储产品名称,如下所示。 您可以通过指定索引来访问特定产品。

//Defining array
String [] arrayOfProducts = new List<String>();
//Adding elements in Array
arrayOfProducts.add('HCL');
arrayOfProducts.add('H2SO4');
arrayOfProducts.add('NACL');
arrayOfProducts.add('H2O');
arrayOfProducts.add('N2');
arrayOfProducts.add('U296');
for (Integer i = 0; i<arrayOfProducts.size(); i++) {
   //This loop will print all the elements in array
   system.debug('Values In Array: '+arrayOfProducts[i]);
}

使用索引访问数组元素

您可以使用索引访问数组中的任何元素,如下所示 -

//Accessing the element in array
//We would access the element at Index 3
System.debug('Value at Index 3 is :'+arrayOfProducts[3]);

Apex - Constants

与任何其他编程语言一样,常量是在声明或赋值后不会更改其值的变量。

在Apex中,当我们想要定义在整个程序执行期间应具有常量值的变量时,使用常量。 Apex常量使用关键字“final”声明。

例子 (Example)

考虑一个CustomerOperationClass类和一个常量变量regularCustomerDiscount -

public class CustomerOperationClass {
   static final Double regularCustomerDiscount = 0.1;
   static Double finalPrice = 0;
   public static Double provideDiscount (Integer price) {
      //calculate the discount
      finalPrice = price - price * regularCustomerDiscount;
      return finalPrice;
   }
}

要查看上述类的输出,您必须在Developer Console匿名窗口中执行以下代码 -

Double finalPrice = CustomerOperationClass.provideDiscount(100);
System.debug('finalPrice '+finalPrice);

Apex - Decision Making

决策结构要求程序员指定一个或多个要由程序评估或测试的条件,以及在条件被确定为真时要执行的语句,以及可选的其他语句,如果条件被确定为假。

在本章中,我们将研究Apex中决策和条件语句的基本和高级结构。 当满足某些条件时,决策是控制执行流程的必要条件。 以下是大多数编程语言中的典型决策结构的一般形式

做决定
Sr.No. 声明和说明
1 if 语句

if语句由一个布尔表达式后跟一个或多个语句组成。

2 if...else 语句

if语句后面可以跟一个可选的else语句,该语句在布尔表达式为false时执行。

3 if...elseif...else statement

if语句后面可以跟一个else if...else语句,这对于使用单个if ... else if语句测试各种条件非常有用。

4 nested if statement

您可以在另一个if or else if语句中使用if or else if语句。

Apex - Loops

当应该以期望的迭代次数重复特定代码时,使用循环。 Apex支持标准的传统for循环以及其他高级类型的循环。 在本章中,我们将详细讨论Apex中的循环。

循环语句允许我们多次执行一个语句或一组语句,以下是大多数编程语言中循环语句的一般说法 -

循环架构

下表列出了处理Apex编程语言中循环要求的不同循环。 单击以下链接以检查其详细信息。

Sr.No. 循环类型和描述
1 for循环

此循环为一组记录中的每个项执行一组语句。

2 SOQL for循环

直接在返回的SOQL查询集上执行一系列语句。

3 类似于Java的循环

以传统的类Java语法执行一系列语句。

4 while 循环

在给定条件为真时重复语句或语句组。 它在执行循环体之前测试条件。

5 做... while循环

像while语句一样,除了它测试循环体末尾的条件。

Apex - Collections

集合是一种可以存储多个记录的变量。 例如,List可以存储多个Account对象的记录。 现在让我们详细了解所有集合类型。

Lists

List可以包含任何数量的基元,集合,sObject,用户定义和内置Apex类型的记录。 这是最重要的收集类型之一,并且它具有一些专门用于List的系统方法。 列表索引始终以0开头。这与Java中的数组同义。 应使用关键字“List”声明列表。

Example

下面是包含原始数据类型列表(字符串)的列表,即城市列表。

List<string> ListOfCities = new List<string>();
System.debug('Value Of ListOfCities'+ListOfCities);

声明list的初始值是可选的。 但是,我们将在此声明初始值。 以下是显示相同的示例。

List<string> ListOfStates = new List<string> {'NY', 'LA', 'LV'};
System.debug('Value ListOfStates'+ListOfStates);

List of Accounts (sObject)

List<account> AccountToDelete = new List<account> (); //This will be null
System.debug('Value AccountToDelete'+AccountToDelete);

我们也可以声明嵌套的List。 它可以达到五个级别。 这称为多维列表。

这是整数集的列表。

List<List<Set<Integer>>> myNestedList = new List<List<Set<Integer>>>();
System.debug('value myNestedList'+myNestedList);

List可以包含任意数量的记录,但是堆大小存在限制,以防止性能问题和独占资源。

列表方法

有可用于列表的方法,我们可以在编程时使用这些方法来实现一些功能,例如计算List的大小,添加元素等。

以下是一些最常用的方法 -

  • size()
  • add()
  • get()
  • clear()
  • set()

以下示例演示了所有这些方法的用法

// Initialize the List
List<string> ListOfStatesMethod = new List<string>();
// This statement would give null as output in Debug logs
System.debug('Value of List'+ ListOfStatesMethod);
// Add element to the list using add method
ListOfStatesMethod.add('New York');
ListOfStatesMethod.add('Ohio');
// This statement would give New York and Ohio as output in Debug logs
System.debug('Value of List with new States'+ ListOfStatesMethod);
// Get the element at the index 0
String StateAtFirstPosition = ListOfStatesMethod.get(0);
// This statement would give New York as output in Debug log
System.debug('Value of List at First Position'+ StateAtFirstPosition);
// set the element at 1 position
ListOfStatesMethod.set(0, 'LA');
// This statement would give output in Debug log
System.debug('Value of List with element set at First Position' + ListOfStatesMethod[0]);
// Remove all the elements in List
ListOfStatesMethod.clear();
// This statement would give output in Debug log
System.debug('Value of List'+ ListOfStatesMethod);

您也可以使用数组表示法来声明List,如下所示,但这不是Apex编程中的一般做法 -

String [] ListOfStates = new List<string>();

Sets

Set是一种集合类型,包含多个无序唯一记录。 集不能有重复记录。 与列表一样,集合可以嵌套。

Example

我们将定义公司销售的产品组合。

Set<string> ProductSet = new Set<string>{'Phenol', 'Benzene', 'H2SO4'};
System.debug('Value of ProductSet'+ProductSet);

集的方法

Set支持我们可以在编程时使用的方法,如下所示(我们正在扩展上面的例子) -

// Adds an element to the set
// Define set if not defined previously
Set<string> ProductSet = new Set<string>{'Phenol', 'Benzene', 'H2SO4'};
ProductSet.add('HCL');
System.debug('Set with New Value '+ProductSet);
// Removes an element from set
ProductSet.remove('HCL');
System.debug('Set with removed value '+ProductSet);
// Check whether set contains the particular element or not and returns true or false
ProductSet.contains('HCL');
System.debug('Value of Set with all values '+ProductSet);

Maps

它是一个键值对,包含每个值的唯一键。 键和值都可以是任何数据类型。

Example

以下示例使用产品代码表示产品名称的映射。

// Initialize the Map
Map<string, string> ProductCodeToProductName = new Map<string, string>
{'1000'=>'HCL', '1001'=>'H2SO4'};
// This statement would give as output as key value pair in Debug log
System.debug('value of ProductCodeToProductName'+ProductCodeToProductName);

地图方法

以下是一些示例,演示了可以与Map一起使用的方法 -

// Define a new map
Map<string, string> ProductCodeToProductName = new Map<string, string>();
// Insert a new key-value pair in the map where '1002' is key and 'Acetone' is value
ProductCodeToProductName.put('1002', 'Acetone');
// Insert a new key-value pair in the map where '1003' is key and 'Ketone' is value
ProductCodeToProductName.put('1003', 'Ketone');
// Assert that the map contains a specified key and respective value
System.assert(ProductCodeToProductName.containsKey('1002'));
System.debug('If output is true then Map contains the key and output is:'
   + ProductCodeToProductName.containsKey('1002'));
// Retrieves a value, given a particular key
String value = ProductCodeToProductName.get('1002');
System.debug('Value at the Specified key using get function: '+value);
// Return a set that contains all of the keys in the map
Set SetOfKeys = ProductCodeToProductName.keySet();
System.debug('Value of Set with Keys '+SetOfKeys);

映射值可能是无序的,因此我们不应该依赖于存储值的顺序,并且总是尝试使用键来访问映射。 映射值可以为null。 声明String时的映射键区分大小写; 例如,ABC和abc将被视为不同的键并被视为唯一键。

Apex - Classes

什么是class?

类是创建对象的模板或蓝图。 对象是类的实例。 这是Class的标准定义。 Apex类与Java类类似。

例如, InvoiceProcessor类描述了具有可以在Invoice上执行的所有方法和操作的类。 如果您创建此类的实例,则它将表示当前在上下文中的单个发票。

创建类

您可以从Developer Console,Force.com Eclipse IDE和Apex Class详细信息页面在Apex中创建类。

从Developer Console

按照以下步骤从Developer Console创建Apex类 -

Step 1 - 转到Name并单击Developer Console。

Step 2 - 单击文件⇒新建,然后单击Apex类。

创建类

来自Force.com IDE

按照以下步骤从Force.com IDE创建一个类 -

Step 1 - 打开Force.com Eclipse IDE

Step 2 - 单击File⇒New⇒ApexClass创建一个新项目。

Step 3 - 提供类的名称,然后单击“确定”。

完成后,将创建新类。

来自Apex Class Detail Page

按照以下步骤从Apex类详细信息页面创建一个类 -

Step 1 - 单击名称⇒设置。

Step 2 - 搜索'Apex Class'并单击链接。 它将打开Apex Class详细信息页面。

从详细信息页面创建Apex类Step1

Step 3 - 单击“新建”,然后提供课程名称,然后单击“保存”。

从详细信息页面创建Apex类Step2

Apex类结构

下面是Apex类定义的示例结构。

Syntax

private | public | global
[virtual | abstract | with sharing | without sharing]
class ClassName [implements InterfaceNameList] [extends ClassName] {
   // Classs Body
}

此定义使用访问修饰符,共享模式,类名和类主体的组合。 我们将进一步研究所有这些选项。

Example

以下是Apex类定义的示例结构 -

public class MySampleApexClass {       //Class definition and body
   public static Integer myValue = 0;  //Class Member variable
   public static String myString = ''; //Class Member variable
   public static Integer getCalculatedValue () {
   // Method definition and body
   // do some calculation
      myValue = myValue+10;
      return myValue;
   }
}

访问修饰符

Private

如果您将访问修饰符声明为“私有”,则此类将仅在本地知道,并且您无法在该特定片段之外访问此类。 默认情况下,类具有此修饰符。

Public

如果将类声明为“Public”,则表示您的组织和定义的命名空间可以访问此类。 通常,大多数Apex类都是使用此关键字定义的。

Global

如果您将该类声明为“全局”,那么无论您的组织如何,所有顶级代码都可以访问该类。 如果您使用Web服务关键字定义了方法,则必须使用global关键字声明包含类。

分享模式

现在让我们讨论不同的分享方式。

与共享

这是Salesforce中Apex类的一个特殊功能。 当使用“With Sharing”关键字指定类时,它具有以下含义:当类将被执行时,它将尊重用户的访问设置和配置文件权限。 假设,用户的操作已触发30条记录的记录更新,但用户只能访问20条记录,并且无法访问10条记录。 然后,如果类正在执行更新记录的操作,则只会更新20条记录,用户可以访问该记录,并且不会更新其余10条记录。 这也称为用户模式。

没有分享

即使用户无法访问30个记录中的10个记录,所有30个记录也将在系统模式下运行时更新,即已使用“无共享”关键字定义。 这称为系统模式。

Virtual

如果使用'virtual'关键字,则表示可以扩展此类,并允许覆盖。 如果需要重写方法,则应使用virtual关键字声明类。

Abstract

如果将类声明为“abstract”,那么它将只包含方法的签名而不包含实际的实现。

类变量

Syntax

[public | private | protected | global] [final] [static] data_type
variable_name [= value]

在上面的语法中 -

  • 变量数据类型和变量名称是必需的
  • 访问修饰符和值是可选的。

Example

public static final Integer myvalue;

Apex - Methods

Class Methods

Apex中的类方法有两个修饰符 - 公共或受保护。 返回类型对于方法是必需的,如果方法没有返回任何内容,则必须提及void作为返回类型。 此外,方法还需要Body。

Syntax

[public | private | protected | global]
[override]
[static]
return_data_type method_name (input parameters) {
   // Method body goes here
}

语法说明

方括号中提到的那些参数是可选的。 但是,以下组件是必不可少的 -

  • return_data_type
  • method_name

类方法的访问修饰符

使用访问修饰符,您可以指定类方法的访问级别。 例如,可以从类的任何位置和类外部访问公共方法。 私有方法只能在课堂上访问。 所有Apex类都可以访问Global,并且可以将其作为其他顶点类可访问的Web服务方法公开。

Example

//Method definition and body
public static Integer getCalculatedValue () {
   //do some calculation
   myValue = myValue+10;
   return myValue;
}

此方法的返回类型为Integer,不带参数。

方法可以具有以下示例中所示的参数 -

// Method definition and body, this method takes parameter price which will then be used 
// in method.
public static Integer getCalculatedValueViaPrice (Decimal price) {
   // do some calculation
   myValue = myValue+price;
   return myValue;
}

类构造函数 (Class Constructors)

构造函数是从类蓝图创建对象时调用的代码。 它与类名具有相同的名称。

我们不需要为每个类定义构造函数,因为默认情况下会调用无参数构造函数。 构造函数对于变量的初始化或在类初始化时要完成进程很有用。 例如,您希望在调用类时将某些Integer变量的值赋值为0。

Example

// Class definition and body
public class MySampleApexClass2 {
   public static Double myValue;   // Class Member variable
   public static String myString;  // Class Member variable
   public MySampleApexClass2 () {
      myValue = 100; //initialized variable when class is called
   }
   public static Double getCalculatedValue () { // Method definition and body
      // do some calculation
      myValue = myValue+10;
      return myValue;
   }
   public static Double getCalculatedValueViaPrice (Decimal price) {
      // Method definition and body
      // do some calculation
      myValue = myValue+price; // Final Price would be 100+100=200.00
      return myValue;
   }
}

您也可以通过构造函数调用类的方法。 在为可视力控制器编程Apex时,这可能很有用。 创建类对象时,将调用构造函数,如下所示 -

// Class and constructor has been instantiated
MySampleApexClass2 objClass = new MySampleApexClass2();
Double FinalPrice = MySampleApexClass2.getCalculatedValueViaPrice(100);
System.debug('FinalPrice: '+FinalPrice);

重载构造函数

构造函数可以重载,即类可以有多个使用不同参数定义的构造函数。

Example

public class MySampleApexClass3 {  // Class definition and body
   public static Double myValue;   // Class Member variable
   public static String myString;  // Class Member variable
   public MySampleApexClass3 () {
      myValue = 100; // initialized variable when class is called
      System.debug('myValue variable with no Overaloading'+myValue);
   }
   public MySampleApexClass3 (Integer newPrice) { // Overloaded constructor
      myValue = newPrice; // initialized variable when class is called
      System.debug('myValue variable with Overaloading'+myValue);
   }
      public static Double getCalculatedValue () { // Method definition and body
      // do some calculation
      myValue = myValue+10;
      return myValue;
   }
   public static Double getCalculatedValueViaPrice (Decimal price) {
      // Method definition and body
      // do some calculation
      myValue = myValue+price;
      return myValue;
   }
}

您可以像我们在前面的示例中执行它一样执行此类。

// Developer Console Code
MySampleApexClass3 objClass = new MySampleApexClass3();
Double FinalPrice = MySampleApexClass3.getCalculatedValueViaPrice(100);
System.debug('FinalPrice: '+FinalPrice);

Apex - Objects

类的实例称为Object。 就Salesforce而言,对象可以是类,也可以创建sObject的对象。

从类创建对象

您可以像在Java或其他面向对象的编程语言中那样创建类的对象。

以下是一个名为MyClass的示例类 -

// Sample Class Example
public class MyClass {
   Integer myInteger = 10;
   public void myMethod (Integer multiplier) {
      Integer multiplicationResult;
      multiplicationResult = multiplier*myInteger;
      System.debug('Multiplication is '+multiplicationResult);
   }
}

这是一个实例类,即,要调用或访问此类的变量或方法,您必须创建此类的实例,然后您可以执行所有操作。

// Object Creation
// Creating an object of class
MyClass objClass = new MyClass();
// Calling Class method using Class instance
objClass.myMethod(100);

sObject创建

sObjects是Salesforce的对象,您可以在其中存储数据。 例如,帐户,联系人等是自定义对象。 您可以创建这些sObject的对象实例。

以下是sObject初始化的示例,并显示了如何使用点表示法访问该特定对象的字段并将值分配给字段。

// Execute the below code in Developer console by simply pasting it
// Standard Object Initialization for Account sObject
Account objAccount = new Account(); // Object initialization
objAccount.Name = 'Testr Account'; // Assigning the value to field Name of Account
objAccount.Description = 'Test Account';
insert objAccount; // Creating record using DML
System.debug('Records Has been created '+objAccount);
// Custom sObject initialization and assignment of values to field
APEX_Customer_c objCustomer = new APEX_Customer_c ();
objCustomer.Name = 'ABC Customer';
objCustomer.APEX_Customer_Decscription_c = 'Test Description';
insert objCustomer;
System.debug('Records Has been created '+objCustomer);

静态初始化

加载类时,静态方法和变量仅初始化一次。 静态变量不会作为Visualforce页面的视图状态的一部分传输。

以下是静态方法和静态变量的示例。

// Sample Class Example with Static Method
public class MyStaticClass {
   Static Integer myInteger = 10;
   public static void myMethod (Integer multiplier) {
      Integer multiplicationResult;
      multiplicationResult = multiplier * myInteger;
      System.debug('Multiplication is '+multiplicationResult);
   }
}
// Calling the Class Method using Class Name and not using the instance object
MyStaticClass.myMethod(100);

Static Variable Use

加载类时,静态变量只会被实例化一次,这种现象可用于避免触发器递归。 静态变量值在同一执行上下文中是相同的,并且正在执行的任何类,触发器或代码都可以引用它并阻止递归。

Apex - Interfaces

接口类似于Apex类,其中没有实现任何方法。 它只包含方法签名,但每个方法的主体都是空的。 要使用接口,另一个类必须通过为接口中包含的所有方法提供主体来实现它。

接口主要用于为代码提供抽象层。 他们将实现与方法的声明分开。

让我们举一个我们的化学公司的例子。 假设我们需要向Premium和普通客户提供折扣,两者的折扣将有所不同。

我们将创建一个名为DiscountProcessor的接口。

// Interface
public interface DiscountProcessor {
   Double percentageDiscountTobeApplied(); // method signature only
}
// Premium Customer Class
public class PremiumCustomer implements DiscountProcessor {
   //Method Call
   public Double percentageDiscountTobeApplied () {
      // For Premium customer, discount should be 30%
      return 0.30;
   }
}
// Normal Customer Class
public class NormalCustomer implements DiscountProcessor {
   // Method Call
   public Double percentageDiscountTobeApplied () {
      // For Premium customer, discount should be 10%
      return 0.10;
   }
}

实现接口时,必须实现该接口的方法。 如果未实现Interface方法,则会引发错误。 如果要为开发人员强制执行方法,则应使用接口。

批处理Apex的标准Salesforce接口

SFDC确实有标准接口,如Database.Batchable,Schedulable等。例如,如果实现Database.Batchable接口,则必须实现接口中定义的三种方法 - 启动,执行和完成。

下面是标准Salesforce提供的Database.Batchable接口的示例,该接口向具有批处理状态的用户发送电子邮件。 该接口有3个方法,Start,Execute和Finish。 使用此接口,我们可以实现Batchable功能,它还提供BatchableContext变量,我们可以使用它来获取有关正在执行的Batch和执行其他功能的更多信息。

global class CustomerProessingBatch implements Database.Batchable<sobject7>,
Schedulable {
   // Add here your email address
   global String [] email = new String[] {'test@test.com'};
   // Start Method
   global Database.Querylocator start (Database.BatchableContext BC) {
      // This is the Query which will determine the scope of Records and fetching the same
      return Database.getQueryLocator('Select id, Name, APEX_Customer_Status__c,
         APEX_Customer_Decscription__c From APEX_Customer__c WHERE createdDate = today
         && APEX_Active__c = true');
   }
   // Execute method
   global void execute (Database.BatchableContext BC, List<sobject> scope) {
      List<apex_customer__c> customerList = new List<apex_customer__c>();
      List<apex_customer__c> updtaedCustomerList = new List<apex_customer__c>();
      for (sObject objScope: scope) {
         // type casting from generic sOject to APEX_Customer__c
         APEX_Customer__c newObjScope = (APEX_Customer__c)objScope ;
         newObjScope.APEX_Customer_Decscription__c = 'Updated Via Batch Job';
         newObjScope.APEX_Customer_Status__c = 'Processed';
         // Add records to the List
         updtaedCustomerList.add(newObjScope);
      }
      // Check if List is empty or not
      if (updtaedCustomerList != null && updtaedCustomerList.size()>0) {
         // Update the Records
         Database.update(updtaedCustomerList); System.debug('List Size
            '+updtaedCustomerList.size());
      }
   }
   // Finish Method
   global void finish(Database.BatchableContext BC) {
      Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
      // get the job Id
      AsyncApexJob a = [Select a.TotalJobItems, a.Status, a.NumberOfErrors,
      a.JobType, a.JobItemsProcessed, a.ExtendedStatus, a.CreatedById,
      a.CompletedDate From AsyncApexJob a WHERE id = :BC.getJobId()];
      System.debug('$$$ Jobid is'+BC.getJobId());
      // below code will send an email to User about the status
      mail.setToAddresses(email);
      // Add here your email address
      mail.setReplyTo('test@test.com');
      mail.setSenderDisplayName('Apex Batch Processing Module');
      mail.setSubject('Batch Processing '+a.Status);
      mail.setPlainTextBody('The Batch Apex job processed
         '+a.TotalJobItems+'batches with '+a.NumberOfErrors+'failures'+'Job Item
         processed are'+a.JobItemsProcessed);
      Messaging.sendEmail(new Messaging.Singleemailmessage [] {mail});
   }
   // Scheduler Method to scedule the class
   global void execute(SchedulableContext sc) {
      CustomerProessingBatch conInstance = new CustomerProessingBatch();
      database.executebatch(conInstance,100);
   }
}

要执行此类,您必须在Developer Console中运行以下代码。

CustomerProessingBatch objBatch = new CustomerProessingBatch ();
Database.executeBatch(objBatch);

Apex - DML

在本章中,我们将讨论如何在Salesforce中执行不同的数据库修改功能。 有两个说我们可以执行功能。

DML声明

DML是为执行插入,更新,删除,复制,恢复记录,合并记录或转换引导操作而执行的操作。

DML是Apex中最重要的部分之一,因为几乎每个业务案例都涉及对数据库的更改和修改。

数据库方法

您可以使用DML语句执行的所有操作也可以使用数据库方法执行。 数据库方法是可用于执行DML操作的系统方法。 与DML语句相比,数据库方法提供了更大的灵活性。

在本章中,我们将讨论使用DML语句的第一种方法。 我们将在后续章节中查看数据库方法。

DML声明

现在让我们再次考虑化学品供应商公司的实例。 我们的发票记录包含状态,已付金额,剩余金额,下一个付款日期和发票编号等字段。 今天创建并且状态为“待定”的发票应更新为“付费”。

插入操作 (Insert Operation)

插入操作用于在数据库中创建新记录。 您可以使用Insert DML语句创建任何标准或自定义对象的记录。

Example

我们可以在APEX_Invoice__c对象中创建新记录,因为每天都会为新客户订单生成新发票。 我们将首先创建一个客户记录,然后我们可以为该新客户记录创建一个发票记录。

// fetch the invoices created today, Note, you must have at least one invoice 
// created today
List<apex_invoice__c> invoiceList = [SELECT id, Name, APEX_Status__c,
   createdDate FROM APEX_Invoice__c WHERE createdDate = today];
// create List to hold the updated invoice records
List<apex_invoice__c> updatedInvoiceList = new List<apex_invoice__c>();
APEX_Customer__c objCust = new APEX_Customer__C();
objCust.Name = 'Test ABC';
//DML for Inserting the new Customer Records
insert objCust;
for (APEX_Invoice__c objInvoice: invoiceList) {
   if (objInvoice.APEX_Status__c == 'Pending') {
      objInvoice.APEX_Status__c = 'Paid';
      updatedInvoiceList.add(objInvoice);
   }
}
// DML Statement to update the invoice status
update updatedInvoiceList;
// Prints the value of updated invoices
System.debug('List has been updated and updated values are' + updatedInvoiceList);
// Inserting the New Records using insert DML statement
APEX_Invoice__c objNewInvoice = new APEX_Invoice__c();
objNewInvoice.APEX_Status__c = 'Pending';
objNewInvoice.APEX_Amount_Paid__c = 1000;
objNewInvoice.APEX_Customer__c = objCust.id;
// DML which is creating the new Invoice record which will be linked with newly
// created Customer record
insert objNewInvoice;
System.debug('New Invoice Id is '+objNewInvoice.id+' and the Invoice Number is'
   + objNewInvoice.Name);

更新操作 (Update Operation)

更新操作是对现有记录执行更新。 在此示例中,我们将现有Invoice记录的Status字段更新为'Paid'。

Example

// Update Statement Example for updating the invoice status. You have to create
and Invoice records before executing this code. This program is updating the
record which is at index 0th position of the List.
// First, fetch the invoice created today
List<apex_invoice__c> invoiceList = [SELECT id, Name, APEX_Status__c,
createdDate FROM APEX_Invoice__c];
List<apex_invoice__c> updatedInvoiceList = new List<apex_invoice__c>();
// Update the first record in the List
invoiceList[0].APEX_Status__c = 'Pending';
updatedInvoiceList.add(invoiceList[0]);
// DML Statement to update the invoice status
update updatedInvoiceList;
// Prints the value of updated invoices
System.debug('List has been updated and updated values of records are' 
   + updatedInvoiceList[0]);

Upsert操作

Upsert Operation用于执行更新操作,如果数据库中不存在要更新的记录,则还要创建新记录。

Example

假设客户对象中的客户记录需要更新。 我们将更新现有客户记录(如果已存在),否则创建新记录。 这将基于字段APEX_External_Id__c的值。 该字段将是我们的字段,用于识别记录是否已存在。

Note - 在执行此代码之前,请在Customer对象中创建一条记录,并将外部Id字段值设置为“12341”,然后执行下面给出的代码 -

// Example for upserting the Customer records
List<apex_customer__c> CustomerList = new List<apex_customer__c>();
for (Integer i = 0; i < 10; i++) {
   apex_customer__c objcust=new apex_customer__c(name = 'Test' +i,
   apex_external_id__c='1234' +i);
   customerlist.add(objcust);
} //Upserting the Customer Records
upsert CustomerList;
System.debug('Code iterated for 10 times and created 9 records as one record with 
   External Id 12341 is already present');
for (APEX_Customer_c objCustomer: CustomerList) {
   if (objCustomer.APEX_External_Id_c == '12341') {
      system.debug('The Record which is already present is '+objCustomer);
   }
}

删除操作 (Delete Operation)

您可以使用Delete DML执行删除操作。

Example

在这种情况下,我们将删除为测试目的而创建的发票,即包含名称为“Test”的发票。

您也可以从Developer控制台执行此代码段,而无需创建类。

// fetch the invoice created today
List<apex_invoice__c> invoiceList = [SELECT id, Name, APEX_Status__c,
createdDate FROM APEX_Invoice__c WHERE createdDate = today];
List<apex_invoice__c> updatedInvoiceList = new List<apex_invoice__c>();
APEX_Customer__c objCust = new APEX_Customer__C();
objCust.Name = 'Test';
// Inserting the Customer Records
insert objCust;
for (APEX_Invoice__c objInvoice: invoiceList) {
   if (objInvoice.APEX_Status__c == 'Pending') {
      objInvoice.APEX_Status__c = 'Paid';
      updatedInvoiceList.add(objInvoice);
   }
}
// DML Statement to update the invoice status
update updatedInvoiceList;
// Prints the value of updated invoices
System.debug('List has been updated and updated values are' + updatedInvoiceList);
// Inserting the New Records using insert DML statement
APEX_Invoice__c objNewInvoice = new APEX_Invoice__c();
objNewInvoice.APEX_Status__c = 'Pending';
objNewInvoice.APEX_Amount_Paid__c = 1000;
objNewInvoice.APEX_Customer__c = objCust.id;
// DML which is creating the new record
insert objNewInvoice;
System.debug('New Invoice Id is' + objNewInvoice.id);
// Deleting the Test invoices from Database
// fetch the invoices which are created for Testing, Select name which Customer Name
// is Test.
List<apex_invoice__c> invoiceListToDelete = [SELECT id FROM APEX_Invoice__c
   WHERE APEX_Customer__r.Name = 'Test'];
// DML Statement to delete the Invoices
delete invoiceListToDelete;
System.debug('Success, '+invoiceListToDelete.size()+' Records has been deleted');

取消删除操作

您可以取消删除已删除且存在于回收站中的记录。 删除的记录所具有的所有关系也将被恢复。

Example

假设,需要恢复上一个示例中删除的记录。 这可以使用以下示例来实现。 此示例中的代码已针对此示例进行了修改。

// fetch the invoice created today
List<apex_invoice__c> invoiceList = [SELECT id, Name, APEX_Status__c,
createdDate FROM APEX_Invoice__c WHERE createdDate = today];
List<apex_invoice__c> updatedInvoiceList = new List<apex_invoice__c>();
APEX_Customer__c objCust = new APEX_Customer__C();
objCust.Name = 'Test';
// Inserting the Customer Records
insert objCust;
for (APEX_Invoice__c objInvoice: invoiceList) {
   if (objInvoice.APEX_Status__c == 'Pending') {
      objInvoice.APEX_Status__c = 'Paid';
      updatedInvoiceList.add(objInvoice);
   }
}
// DML Statement to update the invoice status
update updatedInvoiceList;
// Prints the value of updated invoices
System.debug('List has been updated and updated values are' + updatedInvoiceList);
// Inserting the New Records using insert DML statement
APEX_Invoice__c objNewInvoice = new APEX_Invoice__c();
objNewInvoice.APEX_Status__c = 'Pending';
objNewInvoice.APEX_Amount_Paid__c = 1000;
objNewInvoice.APEX_Customer__c = objCust.id;
// DML which is creating the new record
insert objNewInvoice;
System.debug('New Invoice Id is '+objNewInvoice.id);
// Deleting the Test invoices from Database
// fetch the invoices which are created for Testing, Select name which Customer Name
// is Test.
List<apex_invoice__c> invoiceListToDelete = [SELECT id FROM APEX_Invoice__c
   WHERE APEX_Customer__r.Name = 'Test'];
// DML Statement to delete the Invoices
delete invoiceListToDelete;
system.debug('Deleted Record Count is ' + invoiceListToDelete.size());
System.debug('Success, '+invoiceListToDelete.size() + 'Records has been deleted');
// Restore the deleted records using undelete statement
undelete invoiceListToDelete;
System.debug('Undeleted Record count is '+invoiceListToDelete.size()+'. This should 
   be same as Deleted Record count');

Apex - Database Methods

数据库类方法是使用DML语句的另一种方法,它比插入,更新等DML语句更灵活。

数据库方法和DML语句之间的差异

DML声明 数据库方法
不允许部分更新。 例如,如果列表中有20条记录,则所有记录都将更新或不更新。 允许部分更新。 您可以将Database in Database方法指定为true或false,将true指定为允许部分更新,将false指定为不允许相同。
您无法获取成功列表和失败记录。 您可以像我们在示例中看到的那样获取成功和失败记录的列表。
Example - 插入listName Example - Database.insert(listName,False),其中false表示不允许部分更新。

插入操作 (Insert Operation)

通过数据库方法插入新记录也非常简单和灵活。 让我们考虑前面的场景,其中,我们使用DML语句插入了新记录。 我们将使用数据库方法插入相同的内容。

例子 (Example)

// Insert Operation Using Database methods
// Insert Customer Records First using simple DML Statement. This Customer Record will be
// used when we will create Invoice Records
APEX_Customer__c objCust = new APEX_Customer__C();
objCust.Name = 'Test';
insert objCust; // Inserting the Customer Records
// Insert Operation Using Database methods
APEX_Invoice__c objNewInvoice = new APEX_Invoice__c();
List<apex_invoice__c> InvoiceListToInsert = new List<apex_invoice__c>();
objNewInvoice.APEX_Status__c = 'Pending';
objNewInvoice.APEX_Customer__c = objCust.id;
objNewInvoice.APEX_Amount_Paid__c = 1000;
InvoiceListToInsert.add(objNewInvoice);
Database.SaveResult[] srList = Database.insert(InvoiceListToInsert, false);
// Database method to insert the records in List
// Iterate through each returned result by the method
for (Database.SaveResult sr : srList) {
   if (sr.isSuccess()) {
      // This condition will be executed for successful records and will fetch the ids 
      // of successful records
      System.debug('Successfully inserted Invoice. Invoice ID: ' + sr.getId());
      // Get the invoice id of inserted Account
   } else {
      // This condition will be executed for failed records
      for(Database.Error objErr : sr.getErrors()) {
         System.debug('The following error has occurred.');
         // Printing error message in Debug log
         System.debug(objErr.getStatusCode() + ': ' + objErr.getMessage());
         System.debug('Invoice oject field which are affected by the error:' 
            + objErr.getFields());
      }
   }
}

更新操作 (Update Operation)

现在让我们使用数据库方法考虑我们的业务案例。 假设我们需要更新Invoice对象的状态字段,但同时,我们还需要记录状态,记录ID失败,成功计数等信息。这是使用DML语句无法实现的,因此我们必须使用Database方法获得我们的运营状态。

例子 (Example)

如果Invoice的状态为“待定”且创建日期为今天,我们将更新Invoice的“状态”字段。

下面给出的代码将有助于使用Database.update方法更新Invoice记录。 此外,在执行此代码之前创建一个Invoice记录。

// Code to update the records using the Database methods
List<apex_invoice__c> invoiceList = [SELECT id, Name, APEX_Status__c,
   createdDate FROM APEX_Invoice__c WHERE createdDate = today];
// fetch the invoice created today
List<apex_invoice__c> updatedInvoiceList = new List<apex_invoice__c>();
for (APEX_Invoice__c objInvoice: invoiceList) {
   if (objInvoice.APEX_Status__c == 'Pending') {
      objInvoice.APEX_Status__c = 'Paid';
      updatedInvoiceList.add(objInvoice);    //Adding records to the list
   }
}
Database.SaveResult[] srList = Database.update(updatedInvoiceList, false);
// Database method to update the records in List
// Iterate through each returned result by the method
for (Database.SaveResult sr : srList) {
   if (sr.isSuccess()) {
      // This condition will be executed for successful records and will fetch
      // the ids of successful records
      System.debug('Successfully updated Invoice. Invoice ID is : ' + sr.getId());
   } else {
      // This condition will be executed for failed records
      for(Database.Error objErr : sr.getErrors()) {
         System.debug('The following error has occurred.');
         // Printing error message in Debug log
         System.debug(objErr.getStatusCode() + ': ' + objErr.getMessage());
         System.debug('Invoice oject field which are affected by the error:' 
            + objErr.getFields());
      }
   }
}

我们将仅查看本教程中的Insert和Update操作。 其他操作与这些操作非常相似,我们在上一章中做了些什么。

Apex - SOSL

每个企业或应用程序都将搜索功能作为基本要求之一。 为此,Salesforce.com提供了两种使用SOSL和SOQL的主要方法。 让我们在本章中详细讨论SOSL方法。

SOSL

在整个对象和整个字段中搜索文本字符串将使用SOSL完成。 这是Salesforce对象搜索语言。 它具有跨多个对象搜索特定字符串的功能。

SOSL语句计算为sObject列表,其中,每个列表包含特定sObject类型的搜索结果。 始终以与SOSL查询中指定的顺序相同的顺序返回结果列表。

SOSL查询示例

考虑一个商业案例,其中我们需要开发一个可以搜索指定字符串的程序。 假设,我们需要在Invoice对象的Customer Name字段中搜索字符串'ABC'。 代码如下 -

首先,您必须在Invoice对象中创建一条记录,其客户名称为“ABC”,以便我们可以在搜索时获得有效结果。

// Program To Search the given string in all Object
// List to hold the returned results of sObject generic type
List<list<SObject>> invoiceSearchList = new List<List<SObject>>();
// SOSL query which will search for 'ABC' string in Customer Name field of Invoice Object
invoiceSearchList = [FIND 'ABC*' IN ALL FIELDS RETURNING APEX_Invoice_c
   (Id,APEX_Customer_r.Name)];
// Returned result will be printed
System.debug('Search Result '+invoiceSearchList);
// Now suppose, you would like to search string 'ABC' in two objects,
// that is Invoice and Account. Then for this query goes like this:
// Program To Search the given string in Invoice and Account object,
// you could specify more objects if you want, create an Account with Name as ABC.
// List to hold the returned results of sObject generic type
List<List<SObject>> invoiceAndSearchList = new List<List<SObject>>();
// SOSL query which will search for 'ABC' string in Invoice and in Account object's fields
invoiceAndSearchList = [FIND 'ABC*' IN ALL FIELDS RETURNING APEX_Invoice__c
   (Id,APEX_Customer__r.Name), Account];
// Returned result will be printed
System.debug('Search Result '+invoiceAndSearchList);
// This list will hold the returned results for Invoice Object
APEX_Invoice__c [] searchedInvoice = ((List<APEX_Invoice_c>)invoiceAndSearchList[0]);
// This list will hold the returned results for Account Object
Account [] searchedAccount = ((List<Account>)invoiceAndSearchList[1]);
System.debug('Value of searchedInvoice'+searchedInvoice+'Value of searchedAccount'
   + searchedAccount);

SOQL

这与SOQL几乎相同。 您可以使用它一次只从一个对象获取对象记录。 您可以编写嵌套查询,也可以从您现在查询的父对象或子对象中获取记录。

我们将在下一章中探讨SOQL。

Apex - SOQL

这是专为与SFDC数据库一起使用而设计的Salesforce对象查询语言。 它只能在单个sObject中搜索给定条件的记录。

与SOSL一样,它不能跨多个对象进行搜索,但它确实支持嵌套查询。

SOQL示例

考虑我们正在进行的化学公司示例。 假设,我们需要一个今天创建的记录列表,其客户名称不是“测试”。 在这种情况下,我们将不得不使用下面给出的SOQL查询 -

// fetching the Records via SOQL
List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
InvoiceList = [SELECT Id, Name, APEX_Customer__r.Name, APEX_Status__c FROM
   APEX_Invoice__c WHERE createdDate = today AND APEX_Customer__r.Name != 'Test'];
// SOQL query for given criteria
// Printing the fetched records
System.debug('We have total '+InvoiceList.size()+' Records in List');
for (APEX_Invoice__c objInvoice: InvoiceList) {
   System.debug('Record Value is '+objInvoice); 
   // Printing the Record fetched
}

您可以通过Developer Console中的Query Editor运行SOQL查询,如下所示。

在Developer Console中运行下面给出的查询。 搜索今天创建的发票记录。

SELECT Id, Name, APEX_Customer__r.Name, APEX_Status__c FROM APEX_Invoice__c
   WHERE createdDate = today

您必须选择需要值的字段,否则会导致运行时错误。

遍历关系字段

这是SFDC中最重要的部分之一,因为我们需要遍历父子对象关系

此外,可能存在需要在数据库中插入两个关联对象记录的情况。 例如,Invoice对象与Customer对象有关系,因此一个Customer可以有很多发票。

假设您正在创建发票,然后您需要将此发票与客户相关联。 您可以使用以下代码实现此功能 -

// Now create the invoice record and relate it with the Customer object
// Before executing this, please create a Customer Records with Name 'Customer
// Creation Test'
APEX_Invoice__c objInvoice = new APEX_Invoice__c();
// Relating Invoice to customer via id field of Customer object
objInvoice.APEX_Customer__c = [SELECT id FROM APEX_Customer__c WHERE Name =
   'Customer Creation Test' LIMIT 1].id;
objInvoice.APEX_Status__c = 'Pending';
insert objInvoice;  //Creating Invoice
System.debug('Newly Created Invoice'+objInvoice);  //Newly created invoice

在Developer Console中执行此代码段。 执行后,从开发人员控制台复制发票的ID,然后在SFDC中打开它,如下所示。 您可以看到父记录已分配给发票记录,如下所示。

获取父记录SOQL

获取儿童记录

现在让我们考虑一个例子,其中,与特定客户记录相关的所有发票都需要在一个地方。 为此,您必须知道子关系名称。 要查看子关系名称,请转到子对象的字段详细信息页面,然后选中“子关系”值。 在我们的示例中,最后是__r附加的发票。

例子 (Example)

在此示例中,我们需要设置数据,创建名为“ABC客户”记录的客户,然后向该客户添加3个发票。

现在,我们将获取客户'ABC客户'的发票。 以下是相同的查询 -

// Fetching Child Records using SOQL
List<apex_customer__c> ListCustomers = [SELECT Name, Id, 
   (SELECT id, Name FROM Invoices__r) FROM APEX_Customer__c WHERE Name = 'ABC Customer'];
// Query for fetching the Child records along with Parent
System.debug('ListCustomers '+ListCustomers); // Parent Record
List<apex_invoice__c> ListOfInvoices = ListCustomers[0].Invoices__r;
// By this notation, you could fetch the child records and save it in List
System.debug('ListOfInvoices values of Child '+ListOfInvoices);
// Child records

您可以在Debug日志中看到Record值。

获取父记录

假设您需要获取创建日期为今天的发票客户名称,然后您可以使用下面给出的查询 -

例子 (Example)

获取父记录的值以及子对象。

// Fetching Parent Record Field value using SOQL
List<apex_invoice__c> ListOfInvoicesWithCustomerName = new List<apex_invoice__c>();
ListOfInvoicesWithCustomerName = [SELECT Name, id, APEX_Customer__r.Name 
   FROM APEX_Invoice__c LIMIT 10];
// Fetching the Parent record's values
for (APEX_Invoice__c objInv: ListOfInvoicesWithCustomerName) {
   System.debug('Invoice Customer Name is '+objInv.APEX_Customer__r.Name);
   // Will print the values, all the Customer Records will be printed
}

这里我们使用了符号APEX_Customer__r.Name,其中APEX_Customer__r是父关系名称,这里你必须在Parent字段的末尾附加__r,然后你可以获取父字段值。

聚合函数 (Aggregate Functions)

SOQL确实具有我们在SQL中的聚合函数。 聚合函数允许我们汇总和汇总数据。 现在让我们详细了解这个功能。

假设您想知道我们从客户'ABC客户'获得的平均收入是多少,那么您可以使用此功能来获取平均收入。

例子 (Example)

// Getting Average of all the invoices for a Perticular Customer
AggregateResult[] groupedResults = [SELECT
   AVG(APEX_Amount_Paid__c)averageAmount FROM APEX_Invoice__c WHERE
   APEX_Customer__r.Name = 'ABC Customer'];
Object avgPaidAmount = groupedResults[0].get('averageAmount');
System.debug('Total Average Amount Received From Customer ABC is '+avgPaidAmount);

检查调试日志中的输出。 请注意,包含聚合函数的任何查询都会在AggregateResult对象数组中返回其结果。 AggregateResult是一个只读的sObject,仅用于查询结果。 当我们需要生成大数据报告时,它非常有用。

还有其他聚合函数,您可以使用它们来执行数据汇总。

MIN() - 这可用于查找最小值

MAX() - 这可用于查找最大值。

绑定Apex变量

您可以在SOQL查询中使用Apex变量来获取所需的结果。 顶点变量可以通过冒号(:)表示法引用。

例子 (Example)

// Apex Variable Reference
String CustomerName = 'ABC Customer';
List<apex_customer__c> ListCustomer = [SELECT Id, Name FROM APEX_Customer__c
   WHERE Name = :CustomerName];
// Query Using Apex variable
System.debug('ListCustomer Name'+ListCustomer); // Customer Name

Apex - Security

Apex安全性是指应用安全设置并对运行代码强制执行共享规则的过程。 Apex类具有可通过两个关键字控制的安全设置。

数据安全和共享规则

Apex通常在系统上下文中运行,即当前用户的权限。 在代码执行期间不考虑字段级安全性和共享规则。 只有匿名块代码在执行代码的用户的许可下执行。

我们的Apex代码不应将敏感数据暴露给通过安全和共享设置隐藏的用户。 因此,Apex安全性和执行共享规则是最重要的。

使用共享关键字

如果您使用此关键字,则Apex代码将强制执行当前用户对Apex代码的共享设置。 这不会强制执行配置文件权限,只会强制执行数据级别共享设置。

让我们考虑一个例子,其中,我们的用户可以访问5条记录,但记录总数为10.因此,当Apex类将使用“With Sharing”关键字声明时,它将仅返回用户的5条记录有权访问。

Example

首先,确保您在Customer对象中创建了至少10条记录,其中“Name”为5条记录为“ABC Customer”,其余5条记录为“XYZ Customer”。 然后,创建一个共享规则,该规则将与所有用户共享“ABC客户”。 我们还需要确保将Customer对象的OWD设置为Private。

将下面给出的代码粘贴到Developer Console中的Anonymous块。

// Class With Sharing
public with sharing class MyClassWithSharing {
   // Query To fetch 10 records
   List<apex_customer__c> CustomerList = [SELECT id, Name FROM APEX_Customer__c LIMIT 10];
   public Integer executeQuery () {
      System.debug('List will have only 5 records and the actual records are' 
         + CustomerList.size()+' as user has access to'+CustomerList);
      Integer ListSize = CustomerList.size();
      return ListSize;
   }
}
// Save the above class and then execute as below
// Execute class using the object of class
MyClassWithSharing obj = new MyClassWithSharing();
Integer ListSize = obj.executeQuery();

没有共享关键字

顾名思义,使用此关键字声明的类在系统模式下执行,即,无论用户是否访问记录,查询都将获取所有记录。

// Class Without Sharing
public without sharing class MyClassWithoutSharing {
   List<apex_customer__c> CustomerList = [SELECT id, Name FROM APEX_Customer__c LIMIT 10];
   // Query To fetch 10 records, this will return all the records
   public Integer executeQuery () {
      System.debug('List will have only 5 records and the actula records are'
         + CustomerList.size()+' as user has access to'+CustomerList);
      Integer ListSize = CustomerList.size();
      return ListSize;
   }
}
// Output will be 10 records.

设置Apex类的安全性

您可以为特定配置文件启用或禁用Apex类。 下面给出了相同的步骤。 您可以确定哪个配置文件应该有权访问哪个类。

从类列表页面设置Apex类安全性

Step 1 - 从“设置”中,单击“开发”→“Apex类”。

设置Apex Cass安全性Step1

Step 2 - 单击要限制的类的名称。 我们点击了CustomerOperationClass。

设置Apex Cass安全性Step2

Step 3 - 单击安全性。

设置Apex Cass安全性Step3

Step 4 - 从“可用配置文件”列表中选择要启用的配置文件,然后单击“添加”,或从“启用的配置文件”列表中选择要禁用的配置文件,然后单击“删除”。

设置Apex类安全性Step3

Step 5 - 单击“保存”。

从权限集设置Apex安全性

Step 1 - 从“设置”中,单击“管理用户”→“权限集”。

从权限集Step1设置Apex类安全性

Step 2 - 选择权限集。

从权限集Step2设置Apex类安全性

Step 3 - 单击Apex Class Access。

从权限集设置Apex类安全性Step3

Step 4 - 单击“编辑”。

从权限集设置Apex类安全性Step4

Step 5 - 从Available Apex Classes列表中选择要启用的Apex类,然后单击Add,或从Enabled Apex Classes列表中选择要禁用的Apex类,然后单击remove。

从权限集设置Apex类安全性Step5

Step 6 - 单击“保存”按钮。

Apex - Invoking

Apex调用是指执行Apex类的过程。 Apex类只有在通过下面列出的方法之一调用时才能执行 -

  • 触发器和匿名块

  • 为指定事件调用的触发器

  • 异步Apex

  • 调度Apex类以指定的时间间隔运行,或运行批处理作业

  • Web服务类

  • Apex电子邮件服务类

  • Apex Web Services,允许通过SOAP和REST Web服务公开您的方法

  • Visualforce控制器

  • Apex电子邮件服务处理入站电子邮件

  • 使用JavaScript调用Apex

  • 用于调用Apex中实现的Web服务方法的Ajax工具包

我们现在将了解一些调用Apex的常用方法。

从执行匿名阻止

您可以通过在Developer Console中执行匿名来调用Apex类,如下所示 -

Step 1 - 打开开发者控制台。

Step 2 - 单击Debug。

Apex从执行匿名调用Step1

Step 3 - 执行匿名窗口将打开,如下所示。 现在,点击执行按钮 -

Apex从执行匿名Step2调用

Step 4 - 打开调试日志,它将显示在“日志”窗格中。

Apex从执行匿名调用Step3

来自触发器

您也可以从Trigger调用Apex类。 当指定事件发生时调用触发器,触发器在执行时可以调用Apex类。

以下示例代码显示了在调用Trigger时如何执行类。

例子 (Example)

// Class which will gets called from trigger
public without sharing class MyClassWithSharingTrigger {
   public static Integer executeQuery (List<apex_customer__c> CustomerList) {
      // perform some logic and operations here
      Integer ListSize = CustomerList.size();
      return ListSize;
   }
}
// Trigger Code
trigger Customer_After_Insert_Example on APEX_Customer__c (after insert) {
   System.debug('Trigger is Called and it will call Apex Class');
   MyClassWithSharingTrigger.executeQuery(Trigger.new);  // Calling Apex class and 
                                                         // method of an Apex class
}
// This example is for reference, no need to execute and will have detail look on 
// triggers later chapters.

来自Visualforce页面控制器代码

也可以从Visualforce页面调用Apex类。 我们可以指定控制器或控制器扩展,并调用指定的Apex类。

例子 (Example)

VF Page Code

Apex来自VF页面Step1

Apex Class Code (Controller Extension)

Apex来自VF页面Step2

Apex - Triggers

Apex触发器类似于在特定事件发生时执行的存储过程。 在记录事件发生之前和之后执行触发器。

语法 (Syntax)

trigger triggerName on ObjectName (trigger_events) { Trigger_code_block }

执行触发器

以下是我们可以触发的事件 -

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

触发示例1

假设我们收到业务要求,当客户的“客户状态”字段从“非活动”更改为“活动”时,我们需要创建发票记录。 为此,我们将按照以下步骤在APEX_Customer__c对象上创建一个触发器 -

Step 1 - 转到sObject

Step 2 - 点击客户

Step 3 - 单击Trigger相关列表中的'New'按钮,并添加触发器代码,如下所示。

// Trigger Code
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List InvoiceList = new List();
   for (APEX_Customer__c objCustomer: Trigger.new) {
      if (objCustomer.APEX_Customer_Status__c == 'Active') {
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);
      }
   }
   // DML to insert the Invoice List in SFDC
   insert InvoiceList;
}

说明 (Explanation)

Trigger.new - 这是一个上下文变量,用于存储当前在触发器上下文中的记录,无论是插入还是更新。 在这种情况下,此变量具有已更新的Customer对象的记录。

上下文中还有其他上下文变量 - trigger.old,trigger.newMap,trigger.OldMap。

触发示例2

当客户记录上有更新操作时,将执行上述触发器。 假设,只有当客户状态从非活动状态变为活动状态时才需要插入发票记录,而不是每次都插入; 为此,我们可以使用另一个上下文变量trigger.oldMap ,它将键存储为记录ID,值作为旧记录值存储。

// Modified Trigger Code
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   for (APEX_Customer__c objCustomer: Trigger.new) {
      // condition to check the old value and new value
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
      trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);
      }
   }
   // DML to insert the Invoice List in SFDC
   insert InvoiceList;
}

说明 (Explanation)

我们使用了Trigger.oldMap变量,如前所述,它是一个上下文变量,它存储正在更新的记录的Id和旧值。

Apex - Trigger Design Patterns

设计模式用于使我们的代码更有效,并避免达到调控器限制。 通常,开发人员可以编写低效的代码,这些代码可能导致对象的重复实例化。 这可能导致代码效率低下,性能低下,并可能导致违反调控器限制。 这通常发生在触发器中,因为它们可以对一组记录进行操作。

我们将在本章中看到一些重要的设计模式策略。

批量触发器设计模式

在实际业务案例中,您可能需要一次处理数千条记录。 如果您的触发器不是为处理这种情况而设计的,那么在处理记录时它可能会失败。 在实现触发器时,您需要遵循一些最佳实践。 默认情况下,所有触发器都是批量触发器,并且可以一次处理多个记录。 您应该始终计划一次处理多个记录。

考虑一个业务案例,其中,您需要处理大量记录,并且您已经编写了如下所示的触发器。 这与我们在“客户状态”从“非活动”更改为“活动”时插入发票记录时所采用的示例相同。

// Bad Trigger Example
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   for (APEX_Customer__c objCustomer: Trigger.new) {
      if (objCustomer.APEX_Customer_Status__c == 'Active' && 
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         // condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         insert objInvoice;   //DML to insert the Invoice List in SFDC
      }
   }
}

您现在可以看到已经为循环块编写了DML语句,该循环块仅在处理少量记录时起作用,但是当您处理数百条记录时,它将达到每个事务的DML语句限制,这是governor limit 。 我们将在后续章节中详细介绍Governer Limits。

为避免这种情况,我们必须使触发器有效地一次处理多个记录。

以下示例将帮助您理解相同的内容 -

// Modified Trigger Code-Bulk Trigger
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   for (APEX_Customer__c objCustomer: Trigger.new) {
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         //condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);//Adding records to List
      }
   }
   insert InvoiceList;
   // DML to insert the Invoice List in SFDC, this list contains the all records 
   // which need to be modified and will fire only one DML
}

此触发器仅触发1个DML语句,因为它将在List上运行,并且List具有需要修改的所有记录。

通过这种方式,您可以避免DML语句调控器限制。

触发助手类

在触发器中编写整个代码也不是一个好习惯。 因此,您应该调用Apex类并将处理从Trigger委托给Apex类,如下所示。 Trigger Helper类是执行触发器的所有处理的类。

让我们再次考虑我们的发票记录创建示例。

// Below is the Trigger without Helper class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   for (APEX_Customer__c objCustomer: Trigger.new) {
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         // condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);
      }
   }
   insert InvoiceList; // DML to insert the Invoice List in SFDC
}
// Below is the trigger with helper class
// Trigger with Helper Class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   CustomerTriggerHelper.createInvoiceRecords(Trigger.new, trigger.oldMap);
   // Trigger calls the helper class and does not have any code in Trigger
}

助手班

public class CustomerTriggerHelper {
   public static void createInvoiceRecords (List<apex_customer__c>
   customerList, Map<id, apex_customer__c> oldMapCustomer) {
      List<apex_invoice__c> InvoiceList = new Listvapex_invoice__c>();
      for (APEX_Customer__c objCustomer: customerList) {
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            oldMapCustomer.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            // objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      insert InvoiceList;  // DML to insert the Invoice List in SFDC
   }
}

在这里,所有处理都被委托给了helper类,当我们需要一个新的功能时,我们可以简单地将代码添加到helper类而不修改触发器。

每个sObject上的单个触发器

始终在每个对象上创建一个触发器。 如果达到调控器限制,同一对象上的多个触发器可能会导致冲突和错误。

您可以根据需要使用上下文变量从helper类调用不同的方法。 考虑我们之前的例子。 假设我们的createInvoice方法只应在记录更新时和多个事件上调用。 然后我们可以控制执行如下 -

// Trigger with Context variable for controlling the calling flow
trigger Customer_After_Insert on APEX_Customer__c (after update, after insert) {
   if (trigger.isAfter && trigger.isUpdate) {
      // This condition will check for trigger events using isAfter and isUpdate
      // context variable
      CustomerTriggerHelper.createInvoiceRecords(Trigger.new);
      // Trigger calls the helper class and does not have any code in Trigger
      // and this will be called only when trigger ids after update
   }
}
// Helper Class
public class CustomerTriggerHelper {
   //Method To Create Invoice Records
   public static void createInvoiceRecords (List<apex_customer__c> customerList) {
      for (APEX_Customer__c objCustomer: customerList) {
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      insert InvoiceList; // DML to insert the Invoice List in SFDC
   }
}

Apex - Governer Limits

州长执行限制确保在Force.com多租户平台上有效使用资源。 它是Salesforce.com在代码执行时指定的有效处理限制。

什么是Governer Limits?

众所周知,Apex在多租户环境中运行,即所有客户和组织共享一个资源。 因此,有必要确保没有人垄断资源,因此Salesforce.com创建了一组限制代码执行的限制。 每当超过任何一个调控器限制时,它将抛出错误并停止执行程序。

从开发人员的角度来看,确保我们的代码应该是可扩展的并且不应该达到极限是很重要的。

所有这些限制都适用于每笔交易。 单个触发器执行是一个事务。

正如我们所看到的,触发器设计模式有助于避免限制错误。 我们现在将看到其他重要的限制。

避免SOQL查询限制

每个事务只能发出100个查询,也就是说,当您的代码发出超过100个SOQL查询时,它将抛出错误。

例子 (Example)

此示例显示如何达到SOQL查询限制 -

以下触发器遍历客户列表并使用字符串“Ok to Pay”更新子记录的(Invoice)描述。

// Helper class:Below code needs o be checked.
public class CustomerTriggerHelper {
  public static void isAfterUpdateCall(Trigger.new) {
      createInvoiceRecords(trigger.new);//Method call
      updateCustomerDescription(trigger.new);
   }
   // Method To Create Invoice Records
   public static void createInvoiceRecords (List<apex_customer__c> customerList) {
      for (APEX_Customer__c objCustomer: customerList) {
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      insert InvoiceList; // DML to insert the Invoice List in SFDC
   }
   // Method to update the invoice records
   public static updateCustomerDescription (List<apex_customer__c> customerList) {
      for (APEX_Customer__c objCust: customerList) {
         List<apex_customer__c> invList = [SELECT Id, Name,
            APEX_Description__c FROM APEX_Invoice__c WHERE APEX_Customer__c = :objCust.id];
         // This query will fire for the number of records customer list has and will
         // hit the governor limit when records are more than 100
         for (APEX_Invoice__c objInv: invList) {
            objInv.APEX_Description__c = 'OK To Pay';
            update objInv;
            // Update invoice, this will also hit the governor limit for DML if large
            // number(150) of records are there
         }
      }
   }
}

当调用'updateCustomerDescription'方法并且客户记录数超过100时,它将达到SOQL限制。 为避免这种情况,请勿在For循环中编写SOQL查询。 在这种情况下,SOQL查询已在For循环中编写。

以下示例将说明如何避免DML以及SOQL限制。 我们使用嵌套关系查询来获取发票记录,并使用上下文变量trigger.newMap来获取id和Customer记录的映射。

// SOQL-Good Way to Write Query and avoid limit exception
// Helper Class
public class CustomerTriggerHelper {
   public static void isAfterUpdateCall(Trigger.new) {
      createInvoiceRecords(trigger.new);  //Method call
      updateCustomerDescription(trigger.new, trigger.newMap);
   }
   // Method To Create Invoice Records
   public static void createInvoiceRecords (List<apex_customer__c> customerList) {
      for (APEX_Customer__c objCustomer: customerList) {
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      insert InvoiceList; // DML to insert the Invoice List in SFDC
   }
   // Method to update the invoice records
   public static updateCustomerDescription (List<apex_customer__c>
      customerList, Map<id, apex_customer__c> newMapVariable) {
      List<apex_customer__c> customerListWithInvoice = [SELECT id,
         Name,(SELECT Id, Name, APEX_Description__c FROM APEX_Invoice__r) FROM
         APEX_Customer__c WHERE Id IN :newMapVariable.keySet()];
      // Query will be for only one time and fetches all the records
      List<apex_invoice__c> invoiceToUpdate = new
      List<apex_invoice__c>();
      for (APEX_Customer__c objCust: customerList) {
         for (APEX_Invoice__c objInv: invList) {
            objInv.APEX_Description__c = 'OK To Pay';
            invoiceToUpdate.add(objInv);
            // Add the modified records to List
         }
      }
      update invoiceToUpdate;
   }
}

DML批量电话

此示例显示批量触发器以及触发器助手类模式。 您必须先保存辅助类,然后保存触发器。

Note - 将以下代码粘贴到我们之前创建的“CustomerTriggerHelper”类中。

// Helper Class
public class CustomerTriggerHelper {
   public static void isAfterUpdateCall(List<apex_customer__c> customerList,
      Map<id, apex_customer__c> mapIdToCustomers, Map<id, apex_customer__c>
      mapOldItToCustomers) {
      createInvoiceRecords(customerList, mapOldItToCustomers);   //Method call
      updateCustomerDescription(customerList,mapIdToCustomers,
      mapOldItToCustomers);
   }
   // Method To Create Invoice Records
   public static void createInvoiceRecords (List<apex_customer__c>
      customerList, Map<id, apex_customer__c> mapOldItToCustomers) {
      List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
      List<apex_customer__c> customerToInvoice = [SELECT id, Name FROM
         APEX_Customer__c LIMIT 1];
      for (APEX_Customer__c objCustomer: customerList) {
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            mapOldItToCustomers.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            //condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            objInvoice.APEX_Customer__c = objCustomer.id;
            InvoiceList.add(objInvoice);
         }
      }
      system.debug('InvoiceList&&&'+InvoiceList);
      insert InvoiceList;
      // DML to insert the Invoice List in SFDC. This also follows the Bulk pattern
   }
   // Method to update the invoice records
   public static void updateCustomerDescription (List<apex_customer__c>
      customerList, Map<id, apex_customer__c> newMapVariable, Map<id,
      apex_customer__c> oldCustomerMap) {
      List<apex_customer__c> customerListWithInvoice = [SELECT id,
      Name,(SELECT Id, Name, APEX_Description__c FROM Invoices__r) FROM
         APEX_Customer__c WHERE Id IN :newMapVariable.keySet()];
      // Query will be for only one time and fetches all the records
      List<apex_invoice__c> invoiceToUpdate = new List<apex_invoice__c>();
      List<apex_invoice__c> invoiceFetched = new List<apex_invoice__c>();
      invoiceFetched = customerListWithInvoice[0].Invoices__r;
      system.debug('invoiceFetched'+invoiceFetched);
      system.debug('customerListWithInvoice****'+customerListWithInvoice);
      for (APEX_Customer__c objCust: customerList) {
         system.debug('objCust.Invoices__r'+objCust.Invoices__r);
         if (objCust.APEX_Active__c == true &&
            oldCustomerMap.get(objCust.id).APEX_Active__c == false) {
            for (APEX_Invoice__c objInv: invoiceFetched) {
               system.debug('I am in For Loop'+objInv);
               objInv.APEX_Description__c = 'OK To Pay';
               invoiceToUpdate.add(objInv);
               // Add the modified records to List
            }
         }
      }
     system.debug('Value of List ***'+invoiceToUpdate);
     update invoiceToUpdate;
      // This statement is Bulk DML which performs the DML on List and avoids
      // the DML Governor limit
   }
}
// Trigger Code for this class: Paste this code in 'Customer_After_Insert'
// trigger on Customer Object
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   CustomerTriggerHelper.isAfterUpdateCall(Trigger.new, trigger.newMap,
      trigger.oldMap);
   // Trigger calls the helper class and does not have any code in Trigger
}

其他Salesforce Governor限制

下表列出了重要的Governer Limits。

描述 限制
总堆大小 6 MB/12 MB
发布的DML语句总数 150
单个SOSL查询检索的记录总数 2000
发出的SOSL查询总数 20
Database.getQueryLocator检索的记录总数 10000
SOQL查询检索的记录总数 50000

Apex - Batch Processing

在本章中,我们将了解Apex中的批处理。 考虑一种情况,我们将每天处理大量记录,可能是清理数据或删除一些未使用的数据。

什么是Batch Apex?

Batch Apex是Apex代码的异步执行,专门用于处理大量记录,并且在调控器限制方面比同步代码具有更大的灵活性。

何时使用Batch Apex?

  • 如果您希望每天或甚至在特定的间隔时间内处理大量记录,那么您可以选择Batch Apex。

  • 此外,当您希望操作异步时,您可以实现Batch Apex。 Batch Apex作为必须由开发人员实现的接口公开。 可以使用Apex在运行时以编程方式调用批处理作业。 Batch Apex在小批量记录上运行,覆盖整个记录集并将处理分解为可管理的数据块。

使用Batch Apex

当我们使用Batch Apex时,我们必须实现Salesforce提供的接口Database.Batchable,然后以编程方式调用该类。

您可以按照以下步骤监控class -

要监视或停止批处理Apex批处理作业的执行,请转至设置→监控→Apex作业或作业→Apex作业。

监控Apex Batch Step1

监控Apex Batch Step2

Database.Batchable接口有以下三种需要实现的方法 -

  • Start
  • Execute
  • Finish

现在让我们详细了解每种方法。

Start

Start方法是Database.Batchable接口的三种方法之一。

Syntax

global void execute(Database.BatchableContext BC, list<sobject<) {}

将在批处理作业启动时调用此方法,并收集批处理作业将在其上运行的数据。

请考虑以下几点来理解该方法 -

  • 使用简单查询生成批处理作业中使用的对象范围时,请使用Database.QueryLocator对象。 在这种情况下,将绕过SOQL数据行限制。

  • 如果您有复杂的条件来处理记录,请使用iterable对象。 Database.QueryLocator确定应处理的记录范围。

Execute

现在让我们了解Database.Batchable接口的Execute方法。

Syntax

global void execute(Database.BatchableContext BC, list<sobject<) {}

其中,list

此方法在Start方法之后调用,并执行Batch Job所需的所有处理。

Finish

我们现在将讨论Database.Batchable接口的Finish方法。

Syntax

global void finish(Database.BatchableContext BC) {}

最后调用此方法,您可以执行一些完成活动,例如发送包含有关已处理的批处理作业记录和状态的信息的电子邮件。

批量Apex示例

让我们考虑一下我们现有化学公司的一个例子,并假设我们要求更新已标记为活动且已创建日期的客户记录的客户状态和客户描述字段。 这应该每天进行,并且应该向用户发送关于批处理状态的电子邮件。 将客户状态更新为“已处理”,将客户描述更新为“通过批处理作业更新”。

// Batch Job for Processing the Records
global class CustomerProessingBatch implements Database.Batchable<sobject> {
   global String [] email = new String[] {'test@test.com'};
   // Add here your email address here
   // Start Method
   global Database.Querylocator start (Database.BatchableContext BC) {
      return Database.getQueryLocator('Select id, Name, APEX_Customer_Status__c,
      APEX_Customer_Decscription__c From APEX_Customer__c WHERE createdDate = today
      AND APEX_Active__c = true');
      // Query which will be determine the scope of Records fetching the same
   }
   // Execute method
   global void execute (Database.BatchableContext BC, List<sobject> scope) {
      List<apex_customer__c> customerList = new List<apex_customer__c>();
      List<apex_customer__c> updtaedCustomerList = new List<apex_customer__c>();
      // List to hold updated customer
      for (sObject objScope: scope) {
         APEX_Customer__c newObjScope = (APEX_Customer__c)objScope ;
         // type casting from generic sOject to APEX_Customer__c
         newObjScope.APEX_Customer_Decscription__c = 'Updated Via Batch Job';
         newObjScope.APEX_Customer_Status__c = 'Processed';
         updtaedCustomerList.add(newObjScope); // Add records to the List
         System.debug('Value of UpdatedCustomerList '+updtaedCustomerList);
      }
      if (updtaedCustomerList != null && updtaedCustomerList.size()>0) {
         // Check if List is empty or not
         Database.update(updtaedCustomerList); System.debug('List Size '
          + updtaedCustomerList.size());
         // Update the Records
      }
   }
   // Finish Method
   global void finish(Database.BatchableContext BC) {
      Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
      // Below code will fetch the job Id
      AsyncApexJob a = [Select a.TotalJobItems, a.Status, a.NumberOfErrors,
      a.JobType, a.JobItemsProcessed, a.ExtendedStatus, a.CreatedById,
      a.CompletedDate From AsyncApexJob a WHERE id = :BC.getJobId()];
      // get the job Id
      System.debug('$$$ Jobid is'+BC.getJobId());
      // below code will send an email to User about the status
      mail.setToAddresses(email);
      mail.setReplyTo('test@test.com'); // Add here your email address
      mail.setSenderDisplayName('Apex Batch Processing Module');
      mail.setSubject('Batch Processing '+a.Status);
      mail.setPlainTextBody('The Batch Apex job processed'
         + a.TotalJobItems+'batches with '+a.NumberOfErrors+'failures'+'Job Item
      processed are'+a.JobItemsProcessed);
      Messaging.sendEmail(new Messaging.Singleemailmessage [] {mail});
   }
}

要执行此代码,请先保存它,然后将以下代码粘贴到Execute anonymous中。 这将创建类的对象,Database.execute方法将执行Batch作业。 作业完成后,电子邮件将发送到指定的电子邮件地址。 确保您的客户记录已选中“已Active ”。

// Paste in Developer Console
CustomerProessingBatch objClass = new CustomerProessingBatch();
Database.executeBatch (objClass);

执行此课程后,请检查您提供的电子邮件地址,以便您收到包含信息的电子邮件。 此外,您可以通过监控页面和上面提供的步骤检查批处理作业的状态。

如果检查调试日志,则可以找到列表大小,该大小指示已处理的记录数。

Limitations

我们一次只能处理5个批处理作业。 这是Batch Apex的限制之一。

使用Apex详细信息页面安排Apex批处理作业

您可以通过Apex详细页面安排Apex课程,如下所示 -

Step 1 - 转到设置⇒Apex类,单击Apex类。

从细节页面第1步中推出Apex

Step 2 - 单击Schedule Apex按钮。

从详细页面S2中推出Apex

Step 3 - 提供详细信息。

从细节页面S3uling Speuling Apex

使用可调度接口调度Apex批处理作业

您可以使用可调度接口安排Apex批处理作业,如下所示 -

// Batch Job for Processing the Records
global class CustomerProessingBatch implements Database.Batchable<sobject> {
   global String [] email = new String[] {'test@test.com'};
   // Add here your email address here
   // Start Method
   global Database.Querylocator start (Database.BatchableContext BC) {
      return Database.getQueryLocator('Select id, Name, APEX_Customer_Status__c,
      APEX_Customer_Decscription__c From APEX_Customer__c WHERE createdDate = today
      AND APEX_Active__c = true');
      // Query which will be determine the scope of Records fetching the same
   }
   // Execute method
   global void execute (Database.BatchableContext BC, List<sobject> scope) {
      List<apex_customer__c> customerList = new List<apex_customer__c>();
      List<apex_customer__c> updtaedCustomerList = new
      List<apex_customer__c>();//List to hold updated customer
      for (sObject objScope: scope) {
         APEX_Customer__c newObjScope = (APEX_Customer__c)objScope ;//type
         casting from generic sOject to APEX_Customer__c
         newObjScope.APEX_Customer_Decscription__c = 'Updated Via Batch Job';
         newObjScope.APEX_Customer_Status__c = 'Processed';
         updtaedCustomerList.add(newObjScope);//Add records to the List
         System.debug('Value of UpdatedCustomerList '+updtaedCustomerList);
      }
      if (updtaedCustomerList != null && updtaedCustomerList.size()>0) {
         // Check if List is empty or not
         Database.update(updtaedCustomerList); System.debug('List Size'
            + updtaedCustomerList.size());
         // Update the Records
      }
   }
   // Finish Method
   global void finish(Database.BatchableContext BC) {
      Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
      // Below code will fetch the job Id
      AsyncApexJob a = [Select a.TotalJobItems, a.Status, a.NumberOfErrors,
      a.JobType, a.JobItemsProcessed, a.ExtendedStatus, a.CreatedById,
      a.CompletedDate From AsyncApexJob a WHERE id = :BC.getJobId()];//get the job Id
      System.debug('$$$ Jobid is'+BC.getJobId());
      // below code will send an email to User about the status
      mail.setToAddresses(email);
      mail.setReplyTo('test@test.com');//Add here your email address
      mail.setSenderDisplayName('Apex Batch Processing Module');
      mail.setSubject('Batch Processing '+a.Status);
      mail.setPlainTextBody('The Batch Apex job processed' 
         + a.TotalJobItems+'batches with '+a.NumberOfErrors+'failures'+'Job Item
      processed are'+a.JobItemsProcessed);
      Messaging.sendEmail(new Messaging.Singleemailmessage [] {mail});
   }
   // Scheduler Method to scedule the class
   global void execute(SchedulableContext sc) {
      CustomerProessingBatch conInstance = new CustomerProessingBatch();
      database.executebatch(conInstance,100);
   }
}
// Paste in Developer Console
CustomerProessingBatch objClass = new CustomerProcessingBatch();
Database.executeBatch (objClass);

Apex - Debugging

调试是任何编程开发中的重要部分。 在Apex中,我们有一些可用于调试的工具。 其中之一是system.debug()方法,它在调试日志中打印变量的值和输出。

我们可以使用以下两个工具进行调试 -

  • Developer Console
  • 调试日志

通过Developer Console进行调试

您可以使用Developer控制台并执行匿名功能来调试Apex,如下所示 -

Example

考虑我们现有的获取今天创建的客户记录的示例。 我们只想知道查询是否返回结果,如果是,那么我们将检查List的值。

在执行匿名窗口中粘贴下面给出的代码,并按照我们为打开执行匿名窗口所执行的步骤操作。

Step 1 - 打开Developer控制台

Step 2 - 从“Debug”打开Execute anonymous,如下所示。

打开开发人员控制台,用于类执行Step1

Step 3 - 打开“执行匿名”窗口并粘贴以下代码,然后单击“执行”。

打开开发人员控制台,用于类执行Step2
// Debugging The Apex
List<apex_customer__c> customerList = new List<apex_customer__c>();
customerList = [SELECT Id, Name FROM APEX_Customer__c WHERE CreatedDate =
today];
// Our Query
System.debug('Records on List are '+customerList+' And Records are '+customerList);
// Debug statement to check the value of List and Size

Step 4 - 打开日志,如下所示。

Apex Debugging Devconsole Step1

Step 5 - 在过滤条件中输入“USER”,如下所示。

Step 6 - 打开USER DEBUG语句,如下所示。

通过调试日志进行调试

您也可以通过调试日志调试同一个类。 假设您在Customer对象中有一个触发器并且需要针对某些变量值进行调试,那么您可以通过调试日志执行此操作,如下所示 -

这是触发器代码,如果修改后的客户处于活动状态并且您想要检查当前范围内的变量和记录的值,则会更新“描述”字段 -

trigger CustomerTrigger on APEX_Customer__c (before update) {
   List<apex_customer__c> customerList = new List<apex_customer__c>();
   for (APEX_Customer__c objCust: Trigger.new) {
      System.debug('objCust current value is'+objCust);
      if (objCust.APEX_Active__c == true) {
         objCust.APEX_Customer_Description__c = 'updated';
         System.debug('The record which has satisfied the condition '+objCust);
      }
   }
}

按照下面给出的步骤生成调试日志。

Step 1 - 为您的用户设置调试日志。 转到“设置”并在搜索设置窗口中键入“调试日志”,然后单击“链接”。

通过调试控制台调试Step1

Step 2 - 如下设置调试日志。

通过调试控制台调试Step2

通过调试控制台调试Step3

Step 3 - 输入需要设置的用户名。 在此输入您的姓名。

通过调试控制台调试Step4

Step 4 - 在发生事件时修改客户记录以生成调试日志。

通过调试控制台调试Step5

Step 5 - 现在再次转到调试日志部分。 打开调试日志,然后单击“重置”。

通过调试控制台调试Step6

Step 6 - 单击第一个调试日志的视图链接。

通过调试控制台调试Step7

Step 7 - 使用浏览器搜索搜索字符串'USER',如下所示。

通过调试控制台调试Step8

debug语句将显示我们设置点的字段的值。

Apex - Testing

测试是Apex或任何其他应用程序开发的集成部分。 在Apex中,我们有单独的测试类来开发所有单元测试。

测试类

在SFDC中,代码必须具有75%的代码覆盖率才能部署到Production。 此代码覆盖率由测试类执行。 测试类是测试其他Apex类功能的代码片段。

让我们为我们之前编写的代码编写一个测试类。 我们将编写测试类来覆盖Trigger和Helper类代码。 下面是需要涵盖的触发器和帮助程序类。

// Trigger with Helper Class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   CustomerTriggerHelper.createInvoiceRecords(Trigger.new, trigger.oldMap);
      //Trigger calls the helper class and does not have any code in Trigger
}
// Helper Class:
public class CustomerTriggerHelper {
   public static void createInvoiceRecords (List<apex_customer__c>
      customerList, Map<id, apex_customer__c> oldMapCustomer) {
      List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
      for (APEX_Customer__c objCustomer: customerList) {
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            oldMapCustomer.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            objInvoice.APEX_Customer__c = objCustomer.id;
            InvoiceList.add(objInvoice);
         }
      }
      insert InvoiceList;  // DML to insert the Invoice List in SFDC
   }
}

创建测试类

在本节中,我们将了解如何创建测试类。

数据创建

我们需要在测试类本身中为测试类创建数据。 默认情况下,测试类无权访问组织数据,但如果设置@isTest(seeAllData = true),则它也可以访问组织的数据。

@isTest注释

通过使用此批注,您声明这是一个测试类,它不会计入组织的总代码限制。

testMethod关键字

单元测试方法是不接受参数,不向数据库提交数据,不发送电子邮件,并在方法定义中使用testMethod关键字或isTest批注声明的方法。 此外,必须在测试类中定义测试方法,即使用isTest注释的类。

我们在示例中使用了'myUnitTest'测试方法。

Test.startTest() and Test.stopTest()

这些是可用于测试类的标准测试方法。 这些方法包含我们将模拟测试的事件或操作。 就像在这个例子中一样,我们将测试我们的触发器和辅助类,通过更新记录来模拟触发器,就像我们开始和停止块一样。 这还为启动和停止块中的代码提供单独的调控器限制。

System.assert()

此方法使用实际检查所需的输出。 在这种情况下,我们期望插入一个Invoice记录,因此我们添加了assert来检查相同的内容。

Example

/**
* This class contains unit tests for validating the behavior of Apex classes
* and triggers.
*
* Unit tests are class methods that verify whether a particular piece
* of code is working properly. Unit test methods take no arguments,
* commit no data to the database, and are flagged with the testMethod
* keyword in the method definition.
*
* All test methods in an organization are executed whenever Apex code is deployed
* to a production organization to confirm correctness, ensure code
* coverage, and prevent regressions. All Apex classes are
* required to have at least 75% code coverage in order to be deployed
* to a production organization. In addition, all triggers must have some code coverage.
*
* The @isTest class annotation indicates this class only contains test
* methods. Classes defined with the @isTest annotation do not count against
* the organization size limit for all Apex scripts.
*
* See the Apex Language Reference for more information about Testing and Code Coverage.
*/
@isTest
private class CustomerTriggerTestClass {
   static testMethod void myUnitTest() {
      //Create Data for Customer Objet
      APEX_Customer__c objCust = new APEX_Customer__c();
      objCust.Name = 'Test Customer';
      objCust.APEX_Customer_Status__c = 'Inactive';
      insert objCust;
      // Now, our trigger will fire on After update event so update the Records
      Test.startTest();    // Starts the scope of test
      objCust.APEX_Customer_Status__c = 'Active';
      update objCust;
      Test.stopTest();     // Ends the scope of test
      // Now check if it is giving desired results using system.assert
      // Statement.New invoice should be created
      List<apex_invoice__c> invList = [SELECT Id, APEX_Customer__c FROM
         APEX_Invoice__c WHERE APEX_Customer__c = :objCust.id];
      system.assertEquals(1,invList.size());
      // Check if one record is created in Invoivce sObject
   }
}

运行测试类

按照下面给出的步骤运行测试类 -

Step 1 - 转到Apex类⇒单击类名“CustomerTriggerTestClass”。

Step 2 - 单击Run Test按钮,如图所示。

Apex测试Step1

Step 3 - 检查状态

Apex测试Step2

Step 4 - 现在检查我们编写测试的类和触发器

Class

Apex测试Step3

Trigger

Apex测试Step4

我们的测试成功并完成。

Apex - Deployment

什么是SFDC中的部署?

到目前为止,我们已经在Developer Edition中开发了代码,但在现实生活中,您必须在Sandbox中进行此开发,然后您可能需要将其部署到另一个沙箱或生产环境,这称为部署。 简而言之,这是元数据从一个组织到另一个组织的移动。 这背后的原因是您无法在Salesforce生产组织中开发Apex。 在开发过程中访问系统的实时用户可能会破坏数据稳定性或破坏您的应用程序。

部署过程

可用于部署的工具 -

  • Force.com IDE
  • Change Sets
  • SOAP API
  • Force.com迁移工具

由于我们使用Developer Edition进行开发和学习,因此我们无法使用变更集或其他需要SFDC企业或其他付费版本的工具。 因此,我们将在本教程中详细介绍Force.com IDE部署方法。

Force.com Eclipse IDE

Step 1 - 打开Eclipse并打开需要部署的类触发器。

Eclipse Process Step1

Step 2 - 单击“部署到服务器”后,输入组织的用户名和密码,其中需要部署组件。

Eclipse Process Step2

通过执行上述步骤,您的Apex组件将部署到目标组织。

使用更改集进行部署

您可以通过部署设置将验证规则,工作流规则,Apex类和触发器从一个组织部署到另一个组织。 在这种情况下,组织必须连接。

要打开部署设置,请执行以下步骤。 请记住,Developer Edition中没有此功能 -

Step 1 - 转到“设置”并搜索“部署”。

Step 2 - 单击“Outbound Change Set”以创建要部署的更改集。

Step 3 - 使用“添加”按钮添加要更改集的组件,然后单击“保存”并单击“上载”。

Step 4 - 转到目标组织并单击入站更改集,最后单击部署。

SOAP API调用部署

我们将对这种方法进行一点概述,因为这不是一种常用的方法。

您可以使用下面给出的方法调用来部署元数据。

  • compileAndTest()
  • compileClasses()
  • compileTriggers()

Force.com迁移工具

此工具用于脚本部署。 您必须下载Force.com迁移工具,然后才能执行基于文件的部署。 您可以下载Force.com迁移工具,然后执行脚本部署。

↑回到顶部↑
WIKI教程 @2018