Apex - Guia Rápido

O que é Apex?

Apex é uma linguagem proprietária desenvolvida pela Salesforce.com. De acordo com a definição oficial, o Apex é uma linguagem de programação orientada a objetos fortemente tipada que permite aos desenvolvedores executar as instruções de controle de fluxo e transação no servidor da plataforma Force.com em conjunto com chamadas para a API Force.com.

Ele tem uma sintaxe semelhante a Java e atua como procedimentos armazenados de banco de dados. Ele permite que os desenvolvedores adicionem lógica de negócios à maioria dos eventos do sistema, incluindo cliques em botões, atualizações de registros relacionados e Visualforcepages.Apexo código pode ser iniciado por solicitações de serviço da Web e de gatilhos em objetos. O Apex está incluído na Performance Edition, Unlimited Edition, Enterprise Edition e Developer Edition.

Recursos do Apex como um idioma

Vamos agora discutir os recursos do Apex como uma linguagem -

Integrado

Apex possui suporte integrado para operações DML como INSERT, UPDATE, DELETE e também manipulação de exceções DML. Ele tem suporte para manuseio de consultas SOQL e SOSL sequenciais, que retorna o conjunto de registros de sObject. Estudaremos o objeto, SOQL, SOSL em detalhes nos próximos capítulos.

Sintaxe semelhante ao Java e fácil de usar

O Apex é fácil de usar, pois usa a sintaxe do Java. Por exemplo, declaração de variável, sintaxe de loop e instruções condicionais.

Fortemente integrado com dados

O Apex é focado em dados e projetado para executar várias consultas e instruções DML juntas. Ele emite várias instruções de transação no banco de dados.

Fortemente tipado

Apex é uma linguagem fortemente tipada. Ele usa referência direta a objetos de esquema como sObject e qualquer referência inválida falha rapidamente se for excluída ou se for de tipo de dados incorreto.

Ambiente multilocatário

O Apex é executado em um ambiente multilocatário. Consequentemente, o mecanismo de tempo de execução do Apex é projetado para proteger de perto o código em fuga, evitando que monopolize recursos compartilhados. Qualquer código que viole os limites falha com mensagens de erro fáceis de entender.

Atualiza automaticamente

O Apex é atualizado como parte das versões do Salesforce. Não precisamos atualizá-lo manualmente.

Teste Fácil

O Apex fornece suporte integrado para criação e execução de teste de unidade, incluindo resultados de teste que indicam quanto código é coberto e quais partes de seu código podem ser mais eficientes.

Quando o desenvolvedor deve escolher o Apex?

O Apex deve ser usado quando não formos capazes de implementar a complexa funcionalidade de negócios usando as funcionalidades predefinidas e existentes prontas para uso. Abaixo estão os casos em que precisamos usar o apex sobre a configuração do Salesforce.

Aplicativos Apex

Podemos usar o Apex quando quisermos -

  • Crie serviços da Web com integração de outros sistemas.

  • Crie serviços de e-mail para explosão de e-mail ou configuração de e-mail.

  • Execute validação complexa em vários objetos ao mesmo tempo e também implementação de validação personalizada.

  • Crie processos de negócios complexos que não são suportados pela funcionalidade de fluxo de trabalho ou fluxos existentes.

  • Crie uma lógica transacional personalizada (lógica que ocorre em toda a transação, não apenas com um único registro ou objeto), como usar os métodos de banco de dados para atualizar os registros.

  • Execute alguma lógica quando um registro for modificado ou modifique o registro do objeto relacionado quando houver algum evento que tenha causado o disparo do gatilho.

Estrutura de Trabalho do Apex

Conforme mostrado no diagrama abaixo (Referência: Salesforce Developer Documentation), o Apex é executado inteiramente sob demanda. Plataforma Force.com

Fluxo de Ações

Existem duas sequências de ações quando o desenvolvedor salva o código e quando um usuário final executa alguma ação que invoca o código Apex, conforme mostrado abaixo -

Ação do desenvolvedor

Quando um desenvolvedor grava e salva o código Apex na plataforma, o servidor de aplicativos da plataforma primeiro compila o código em um conjunto de instruções que podem ser entendidas pelo interpretador de tempo de execução do Apex e, em seguida, salva essas instruções como metadados.

Ação do usuário final

Quando um usuário final dispara a execução do Apex, clicando em um botão ou acessando uma página do Visualforce, o servidor de aplicativos da plataforma recupera as instruções compiladas dos metadados e as envia por meio do interpretador de tempo de execução antes de retornar o resultado. O usuário final não observa diferenças no tempo de execução em comparação com a solicitação de plataforma de aplicativo padrão.

Como o Apex é a linguagem proprietária da Salesforce.com, ele não oferece suporte a alguns recursos que uma linguagem de programação geral oferece. A seguir estão alguns recursos que o Apex não oferece suporte -

  • Não pode mostrar os elementos na interface do usuário.

  • Você não pode alterar a funcionalidade fornecida pelo SFDC padrão e também não é possível impedir a execução da funcionalidade padrão.

  • A criação de vários threads também não é possível, pois podemos fazê-lo em outras línguas.

Compreendendo a sintaxe do Apex

O código do Apex normalmente contém muitas coisas com as quais podemos estar familiarizados em outras linguagens de programação.

Declaração de Variável

Como linguagem fortemente tipada, você deve declarar todas as variáveis ​​com tipo de dados no Apex. Conforme visto no código abaixo (captura de tela abaixo), lstAcc é declarado com o tipo de dados como Lista de contas.

Consulta SOQL

Isso será usado para buscar os dados do banco de dados Salesforce. A consulta mostrada na captura de tela abaixo está buscando dados do objeto Conta.

Declaração de Loop

Essa instrução de loop é usada para iterar em uma lista ou em um trecho de código por um determinado número de vezes. No código mostrado na imagem abaixo, a iteração será igual ao número de registros que temos.

Declaração de controle de fluxo

A instrução If é usada para controle de fluxo neste código. Com base em certas condições, é decidido se vai para a execução ou para parar a execução de uma parte específica do código. Por exemplo, no código mostrado abaixo, ele verifica se a lista está vazia ou se contém registros.

Declaração DML

Executa a operação de inserção, atualização, upsert e exclusão de registros nos registros do banco de dados. Por exemplo, o código fornecido a seguir ajuda na atualização de contas com novo valor de campo.

A seguir está um exemplo de como será a aparência de um trecho de código Apex. Vamos estudar todos esses conceitos de programação do Apex mais adiante neste tutorial.

Neste capítulo, entenderemos o ambiente para o desenvolvimento do nosso Salesforce Apex. Presume-se que você já tenha uma edição do Salesforce configurada para fazer o desenvolvimento do Apex.

Você pode desenvolver o código Apex na edição Sandbox ou Developer do Salesforce. Uma organização Sandbox é uma cópia de sua organização na qual você pode escrever código e testá-lo sem correr o risco de modificação de dados ou perturbar a funcionalidade normal. De acordo com a prática industrial padrão, você deve desenvolver o código no Sandbox e então implantá-lo no ambiente de Produção.

Para este tutorial, usaremos a edição Developer do Salesforce. Na edição Developer, você não terá a opção de criar uma organização Sandbox. Os recursos do Sandbox estão disponíveis em outras edições do Salesforce.

Ferramentas de desenvolvimento de código Apex

Em todas as edições, podemos usar qualquer uma das três ferramentas a seguir para desenvolver o código -

  • Console do desenvolvedor Force.com
  • IDE Force.com
  • Editor de código na interface de usuário do Salesforce

Note − Estaremos utilizando o console do desenvolvedor em todo o nosso tutorial para a execução do código, pois é simples e fácil de usar para o aprendizado.

Console do desenvolvedor Force.com

O Developer Console é um ambiente de desenvolvimento integrado com uma coleção de ferramentas que você pode usar para criar, depurar e testar aplicativos em sua organização do Salesforce.

Siga estas etapas para abrir o console do desenvolvedor -

Step 1 − Vá para Nome → Console do desenvolvedor

Step 2 − Clique em "Developer Console" e uma janela aparecerá como na imagem a seguir.

A seguir estão algumas operações que podem ser realizadas usando o console do desenvolvedor.

  • Writing and compiling code −Você pode escrever o código usando o editor de código-fonte. Quando você salva um gatilho ou classe, o código é compilado automaticamente. Quaisquer erros de compilação serão relatados.

  • Debugging −Você pode escrever o código usando o editor de código-fonte. Quando você salva um gatilho ou classe, o código é compilado automaticamente. Quaisquer erros de compilação serão relatados.

  • Testing − Você pode visualizar os logs de depuração e definir pontos de verificação que auxiliam na depuração.

  • Checking performance −Você pode executar testes de classes de teste específicas ou todas as classes em sua organização e pode visualizar os resultados do teste. Além disso, você pode inspecionar a cobertura do código.

  • SOQL queries − Você pode inspecionar os logs de depuração para localizar gargalos de desempenho.

  • Color coding and autocomplete − O editor de código-fonte usa um esquema de cores para facilitar a leitura dos elementos do código e fornece preenchimento automático para nomes de classes e métodos.

Executando código no console do desenvolvedor

Todos os trechos de código mencionados neste tutorial precisam ser executados no console do desenvolvedor. Siga estas etapas para executar as etapas no console do desenvolvedor.

Step 1 - Faça login no Salesforce.com usando login.salesforce.com. Copie os trechos de código mencionados no tutorial. Por enquanto, usaremos o seguinte código de exemplo.

String myString = 'MyString';
System.debug('Value of String Variable'+myString);

Step 2 - Para abrir o console do desenvolvedor, clique em Nome → Console do desenvolvedor e clique em Executar anônimo conforme mostrado abaixo.

Step 3 - Nesta etapa, uma janela aparecerá e você poderá colar o código lá.

Step 4 - Quando clicamos em Execute, os logs de depuração serão abertos. Quando o log aparecer na janela conforme mostrado abaixo, clique no registro do log.

Em seguida, digite 'USER' na janela conforme mostrado abaixo e a instrução de saída aparecerá na janela de depuração. Esta instrução 'USER' é usada para filtrar a saída.

Então, basicamente, você seguirá todas as etapas mencionadas acima para executar qualquer trecho de código neste tutorial.

Exemplo de desenvolvimento de aplicativo empresarial

Para o nosso tutorial, estaremos implementando o aplicativo CRM para uma empresa de processamento e equipamentos químicos. Esta empresa lida com fornecedores e presta serviços. Trabalharemos pequenos trechos de código relacionados a este exemplo ao longo de nosso tutorial para entender cada conceito em detalhes.

Para executar o código neste tutorial, você precisará ter dois objetos criados: objetos Cliente e Fatura. Se você já sabe como criar esses objetos no Salesforce, pode pular as etapas abaixo. Caso contrário, você pode seguir o guia passo a passo abaixo.

Criando Objeto de Cliente

Estaremos configurando o objeto Cliente primeiro.

Step 1- Vá para Configuração e procure por 'Objeto' conforme mostrado abaixo. Em seguida, clique no link Objetos conforme mostrado abaixo.

Step 2 - Assim que a página do objeto for aberta, clique no botão 'Create New Object'como mostrado abaixo.

Step 3- Após clicar no botão, a página de criação do novo objeto aparecerá e, em seguida, insira todos os detalhes do objeto conforme inserido abaixo. O nome do objeto deve ser Cliente. Você só precisa inserir as informações no campo conforme mostrado na imagem abaixo e manter as outras coisas padrão como estão.

Insira as informações e clique no botão 'Salvar' -

Seguindo as etapas acima, criamos com sucesso o objeto Cliente.

Criação do objeto Campos personalizados para o cliente

Agora que temos nosso objeto Cliente configurado, iremos criar um campo 'Ativo' e então você pode criar os outros campos seguindo etapas semelhantes. O nome e o nome da API do campo serão fornecidos na captura de tela.

Step 1- Estaremos criando um campo denominado 'Ativo' do tipo de dados como Caixa de Seleção. Vá para Configuração e clique nele.

Step 2 - Procure por 'Objeto' conforme mostrado abaixo e clique nele.

Step 3 - Clique no objeto 'Cliente'.

Step 4 - Depois de clicar no link do objeto Cliente e a página de detalhes do objeto aparecer, clique no botão Novo.

Step 5 - Agora, selecione o tipo de dados como Caixa de seleção e clique em Avançar.

Step 6 - Insira o nome do campo e o rótulo conforme mostrado abaixo.

Step 7 - Clique em Visível e em Avançar.

Step 8 - Agora clique em 'Salvar'.

Seguindo as etapas acima, nosso campo personalizado 'Ativo' é criado. Você deve seguir todas as etapas de criação de campo personalizado acima para os campos restantes. Esta é a visão final do objeto do cliente, uma vez que todos os campos são criados -

Criação de objeto de fatura

Step 1 - Vá para Configuração e pesquise por 'Objeto' e clique no link Objetos conforme mostrado abaixo.

Step 2 - Assim que a página do objeto for aberta, clique no botão 'Criar novo objeto' conforme mostrado abaixo.

Step 3- Após clicar no botão, a página de criação do novo objeto aparecerá conforme mostrado na imagem abaixo. Você precisa inserir os detalhes aqui. O nome do objeto deve ser Fatura. Isso é semelhante a como criamos o objeto Cliente anteriormente neste tutorial.

Step 4 - Insira as informações conforme mostrado abaixo e clique no botão 'Salvar'.

Seguindo essas etapas, seu objeto Fatura será criado.

Criação de campos personalizados para objeto de fatura

Estaremos criando o campo Descrição no objeto Nota Fiscal conforme mostrado abaixo -

Step 1 - Vá em Setup e clique nele.

Step 2 - Procure por 'Objeto' conforme mostrado abaixo e clique nele.

Step 3 - Clique no objeto 'Fatura'.

Em seguida, clique em 'Novo'.

Step 4 - Selecione o tipo de dados como Área de Texto e clique no botão Avançar.

Step 5 - Insira as informações fornecidas abaixo.

Step 6 - Clique em Visível e em Avançar.

Step 7 - Clique em Salvar.

Da mesma forma, você pode criar os outros campos no objeto Fatura.

Com isso, criamos os objetos necessários para este tutorial. Estaremos aprendendo vários exemplos nos capítulos subsequentes com base nesses objetos.

Compreendendo os tipos de dados

A linguagem do Apex é fortemente tipada para que cada variável no Apex seja declarada com o tipo de dados específico. Todas as variáveis ​​do apex são inicializadas como nulas inicialmente. É sempre recomendado para um desenvolvedor certificar-se de que os valores apropriados sejam atribuídos às variáveis. Caso contrário, essas variáveis, quando usadas, lançarão exceções de ponteiro nulo ou quaisquer exceções não tratadas.

Apex suporta os seguintes tipos de dados -

  • Primitivo (inteiro, duplo, longo, data, data e hora, string, ID ou booleano)

  • Coleções (listas, conjuntos e mapas) (a serem abordadas no Capítulo 6)

  • sObject

  • Enums

  • Classes, objetos e interfaces (a serem abordados nos capítulos 11, 12 e 13)

Neste capítulo, veremos todos os tipos de dados primitivos, sObjects e Enums. Estaremos olhando Coleções, Classes, Objetos e Interfaces nos próximos capítulos, uma vez que são tópicos-chave a serem aprendidos individualmente.

Tipos de dados primitivos

Nesta seção, discutiremos os tipos de dados primitivos suportados pelo Apex.

Inteiro

Um número de 32 bits que não inclui nenhum ponto decimal. O intervalo de valores para isso começa em -2.147.483.648 e o valor máximo é de até 2.147.483.647.

Example

Queremos declarar uma variável que armazenará a quantidade de barris que precisa ser enviada ao comprador da planta de processamento químico.

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

o System.debug() A função imprime o valor da variável para que possamos usar isso para depurar ou saber qual valor a variável possui atualmente.

Cole o código acima no console do desenvolvedor e clique em Executar. Assim que os logs forem gerados, ele mostrará o valor da variável "barrelNumbers" como 1000.

boleano

Essa variável pode ser verdadeira, falsa ou nula. Muitas vezes, esse tipo de variável pode ser usado como sinalizador na programação para identificar se a condição particular está definida ou não.

Example

Se o booleano shipmentDispatched for definido como verdadeiro, ele pode ser declarado como -

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

Encontro

Este tipo de variável indica uma data. Isso só pode armazenar a data e não a hora. Para salvar a data junto com a hora, precisaremos armazená-la na variável de DateTime.

Example

Considere o exemplo a seguir para entender como a variável Date funciona.

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

Grandes

Este é um número de 64 bits sem um ponto decimal. Isso é usado quando precisamos de um intervalo de valores mais amplo do que aqueles fornecidos por Integer.

Example

Se a receita da empresa for armazenada, usaremos o tipo de dados como Longo.

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

Objeto

Podemos nos referir a isso como qualquer tipo de dados compatível com o Apex. Por exemplo, a variável de classe pode ser objeto dessa classe, e o tipo genérico sObject também é um objeto e o tipo de objeto específico da mesma forma como Conta também é um objeto.

Example

Considere o exemplo a seguir para entender como a variável bject funciona.

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

Note - Você também pode criar um objeto de classe predefinida, conforme mostrado abaixo -

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

Este é o objeto de classe que será usado como variável de classe.

Corda

String é qualquer conjunto de caracteres entre aspas simples. Não há limite para o número de caracteres. Aqui, o tamanho do heap será usado para determinar o número de caracteres. Isso restringe o monopólio de recursos do programa Apex e também garante que ele não fique muito grande.

Example

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

Tempo

Esta variável é usada para armazenar o tempo particular. Esta variável deve sempre ser declarada com o método estático do sistema.

Blob

O Blob é uma coleção de dados binários que são armazenados como objeto. Isso será usado quando quisermos armazenar o anexo no Salesforce em uma variável. Este tipo de dados converte os anexos em um único objeto. Se o blob for convertido em uma string, então podemos usar os métodos toString e valueOf para o mesmo.

objeto

Este é um tipo de dados especial no Salesforce. É semelhante a uma tabela em SQL e contém campos que são semelhantes a colunas em SQL. Existem dois tipos de objetos - padrão e personalizado.

Por exemplo, Account é um sObject padrão e qualquer outro objeto definido pelo usuário (como o objeto Customer que criamos) é um sObject personalizado.

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 é um tipo de dados abstrato que armazena um valor de um conjunto finito de identificadores especificados. Você pode usar a palavra-chave Enum para definir um Enum. Enum pode ser usado como qualquer outro tipo de dados no Salesforce.

Example

Você pode declarar os nomes possíveis de Composto Químico executando o seguinte código -

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

Java e Apex são semelhantes em muitos aspectos. A declaração de variável em Java e Apex também é praticamente a mesma. Discutiremos alguns exemplos para entender como declarar variáveis ​​locais.

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

Observe que todas as variáveis ​​são atribuídas com o valor null.

Declaring Variables

Você pode declarar as variáveis ​​no Apex como String e Integer da seguinte maneira -

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

Apex variables are Case-Insensitive

Isso significa que o código fornecido a seguir gerará um erro, pois a variável 'm' foi declarada duas vezes e ambas serão tratadas como iguais.

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

Uma variável Apex é válida a partir do ponto em que é declarada no código. Portanto, não é permitido redefinir a mesma variável novamente e no bloco de código. Além disso, se você declarar qualquer variável em um método, esse escopo de variável será limitado apenas a esse método específico. No entanto, as variáveis ​​de classe podem ser acessadas em toda a classe.

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

String no Apex, como em qualquer outra linguagem de programação, é qualquer conjunto de caracteres sem limite de caracteres.

Example

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

Métodos de String

A classe String no Salesforce tem muitos métodos. Vamos dar uma olhada em alguns dos métodos de string mais importantes e freqüentemente usados ​​neste capítulo.

contém

Este método retornará verdadeiro se a string fornecida contiver a substring mencionada.

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

é igual a

Este método retornará verdadeiro se a string fornecida e a string passada no método tiverem a mesma sequência binária de caracteres e não forem nulos. Você também pode comparar o ID do registro SFDC usando este método. Este método diferencia maiúsculas de minúsculas.

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

Este método retornará verdadeiro se stringtoCompare tiver a mesma sequência de caracteres que a string fornecida. No entanto, esse método não faz distinção entre maiúsculas e minúsculas.

Syntax

public Boolean equalsIgnoreCase(String stringtoCompare)

Example

O código a seguir retornará verdadeiro porque os caracteres e a sequência da string são iguais, ignorando a distinção entre maiúsculas e minúsculas.

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

retirar

Este método remove a string fornecida em stringToRemove da string fornecida. Isso é útil quando você deseja remover alguns caracteres específicos da string e não tem conhecimento do índice exato dos caracteres a serem removidos. Este método faz distinção entre maiúsculas e minúsculas e não funcionará se a mesma sequência de caracteres ocorrer, mas as maiúsculas e minúsculas forem diferentes.

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

Este método remove a string fornecida em stringToRemove da string fornecida, mas apenas se ocorrer no final. Este método não faz distinção entre maiúsculas e minúsculas.

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

começa com

Este método retornará verdadeiro se a string fornecida começar com o prefixo fornecido no método.

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

As matrizes no Apex são basicamente as mesmas que as listas no Apex. Não há distinção lógica entre os Arrays e as Listas, pois sua estrutura de dados e métodos internos também são os mesmos, mas a sintaxe do array é um pouco tradicional como o Java.

Abaixo está a representação de uma matriz de produtos -

Index 0 - HCL

Index 1 - H2SO4

Index 2 - NACL

Index 3 - H2O

Index 4 - N2

Index 5 - U296

Sintaxe

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

Exemplo

Suponha que tenhamos que armazenar o nome de nossos produtos - podemos usar o Array em que armazenaremos os nomes dos produtos conforme mostrado abaixo. Você pode acessar o produto específico especificando o índice.

//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]);
}

Acessando o elemento da matriz usando o índice

Você pode acessar qualquer elemento na matriz usando o índice conforme mostrado abaixo -

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

Como em qualquer outra linguagem de programação, Constantes são as variáveis ​​que não mudam de valor depois de declaradas ou atribuídas a um valor.

No Apex, constantes são usadas quando queremos definir variáveis ​​que devem ter valor constante ao longo da execução do programa. As constantes do Apex são declaradas com a palavra-chave 'final'.

Exemplo

Considere um CustomerOperationClass classe e uma variável constante regularCustomerDiscount dentro dele -

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

Para ver o resultado da classe acima, você deve executar o seguinte código na janela anônima do console do desenvolvedor -

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

Estruturas de tomada de decisão requerem que o programador especifique uma ou mais condições a serem avaliadas ou testadas pelo programa, juntamente com uma instrução ou instruções a serem executadas se a condição for determinada como verdadeira e, opcionalmente, outras instruções a serem executadas se o condição é determinada como falsa.

Neste capítulo, estudaremos a estrutura básica e avançada de tomada de decisão e instruções condicionais no Apex. A tomada de decisão é necessária para controlar o fluxo de execução quando determinada condição é atendida ou não. A seguir está a forma geral de uma estrutura típica de tomada de decisão encontrada na maioria das linguagens de programação

Sr. Não. Declaração e descrição
1 declaração if

Uma instrução if consiste em uma expressão booleana seguida por uma ou mais instruções.

2 declaração if ... else

Uma declaração if pode ser seguida por um opcional else instrução, que é executada quando a expressão booleana é falsa.

3 declaração if ... elseif ... else

Uma declaração if pode ser seguida por um opcional else if...else , que é muito útil para testar várias condições usando uma única instrução if ... else if.

4 declaração if aninhada

Você pode usar um if or else if declaração dentro de outra if or else if afirmações).

Os loops são usados ​​quando uma parte específica do código deve ser repetida com o número desejado de iterações. O Apex suporta o loop for tradicional padrão, bem como outros tipos avançados de Loops. Neste capítulo, discutiremos em detalhes sobre os Loops no Apex.

Uma instrução de loop nos permite executar uma instrução ou grupo de instruções várias vezes e, a seguir, está o modelo geral de uma instrução de loop na maioria das linguagens de programação -

As tabelas a seguir listam os diferentes Loops que tratam dos requisitos de loop na linguagem de programação do Apex. Clique nos links a seguir para verificar seus detalhes.

Sr. Não. Tipo e descrição de loop
1 para loop

Este loop executa um conjunto de instruções para cada item em um conjunto de registros.

2 SOQL para loop

Execute uma sequência de instruções diretamente sobre o conjunto retornado da consulta SOQL.

3 For loop semelhante a Java

Execute uma sequência de instruções na sintaxe tradicional do tipo Java.

4 loop while

Repete uma declaração ou grupo de declarações enquanto uma determinada condição for verdadeira. Ele testa a condição antes de executar o corpo do loop.

5 fazer ... loop while

Como uma instrução while, exceto que testa a condição no final do corpo do loop.

Coleções são um tipo de variável que pode armazenar vários números de registros. Por exemplo, Lista pode armazenar vários números de registros do objeto Conta. Vamos agora ter uma visão geral detalhada de todos os tipos de coleção.

Listas

A lista pode conter qualquer número de registros de primitivos, coleções, sObjects, definidos pelo usuário e construídos no tipo Apex. Este é um dos tipos de coleção mais importantes e também possui alguns métodos de sistema que foram ajustados especificamente para uso com List. O índice de lista sempre começa com 0. Isso é sinônimo de array em Java. Uma lista deve ser declarada com a palavra-chave 'Lista'.

Example

Abaixo está a lista que contém a Lista de um tipo de dado primitivo (string), que é a lista de cidades.

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

Declarar os valores iniciais da lista é opcional. No entanto, iremos declarar os valores iniciais aqui. A seguir está um exemplo que mostra o mesmo.

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

Lista de contas (sObject)

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

Podemos declarar a Lista aninhada também. Pode ir até cinco níveis. Isso é chamado de lista multidimensional.

Esta é a lista de um conjunto de inteiros.

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

A lista pode conter qualquer número de registros, mas há uma limitação no tamanho do heap para evitar o problema de desempenho e monopolizar os recursos.

Métodos para listas

Existem métodos disponíveis para listas que podemos utilizar durante a programação para obter algumas funcionalidades, como calcular o tamanho de uma lista, adicionar um elemento, etc.

A seguir estão alguns métodos usados ​​com mais frequência -

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

O exemplo a seguir demonstra o uso de todos esses métodos

// 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);

Você também pode usar a notação de matriz para declarar a Lista, conforme fornecido abaixo, mas isso não é uma prática geral na programação do Apex -

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

Jogos

Um conjunto é um tipo de coleção que contém vários números de registros exclusivos não ordenados. Um Conjunto não pode ter registros duplicados. Como as listas, os conjuntos podem ser aninhados.

Example

Estaremos definindo o conjunto de produtos que a empresa está vendendo.

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

Métodos para conjuntos

Set suporta métodos que podemos utilizar durante a programação, conforme mostrado abaixo (estamos estendendo o exemplo acima) -

// 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);

Mapas

É um par de valores-chave que contém a chave exclusiva para cada valor. Tanto a chave quanto o valor podem ser de qualquer tipo de dados.

Example

O exemplo a seguir representa o mapa do Nome do Produto com o código do Produto.

// 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);

Métodos para mapas

A seguir estão alguns exemplos que demonstram os métodos que podem ser usados ​​com 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);

Os valores do mapa podem estar desordenados e, portanto, não devemos confiar na ordem em que os valores são armazenados e tentar acessar o mapa sempre usando chaves. O valor do mapa pode ser nulo. As chaves do mapa quando declaradas String diferenciam maiúsculas de minúsculas; por exemplo, ABC e abc serão considerados chaves diferentes e tratados como únicos.

O que é uma classe?

Uma classe é um modelo ou projeto a partir do qual os objetos são criados. Um objeto é uma instância de uma classe. Esta é a definição padrão de Classe. As classes Apex são semelhantes às classes Java.

Por exemplo, InvoiceProcessorclass descreve a classe que contém todos os métodos e ações que podem ser executados na Nota Fiscal. Se você criar uma instância dessa classe, ela representará a única fatura que está atualmente no contexto.

Criação de classes

Você pode criar classes no Apex a partir do console do desenvolvedor, Force.com Eclipse IDE e também da página de detalhes Apex Class.

Do console do desenvolvedor

Siga estas etapas para criar uma classe Apex no console do desenvolvedor -

Step 1 - Vá para Nome e clique em Console do desenvolvedor.

Step 2 - Clique em Arquivo ⇒ Novo e depois clique na classe Apex.

Do IDE da Force.com

Siga estas etapas para criar uma classe do Force.com IDE -

Step 1 - Abra Force.com Eclipse IDE

Step 2 - Crie um Novo Projeto clicando em Arquivo ⇒ Novo ⇒ Classe do Apex.

Step 3 - Forneça o Nome da Classe e clique em OK.

Feito isso, a nova classe será criada.

Da página de detalhes da classe Apex

Siga estas etapas para criar uma classe na página de detalhes da classe Apex -

Step 1 - Clique em Nome ⇒ Configuração.

Step 2- Pesquise 'Apex Class' e clique no link. Isso abrirá a página de detalhes da classe Apex.

Step 3 - Clique em 'Novo' e, a seguir, forneça o nome da turma e clique em Salvar.

Estrutura da Classe Apex

Abaixo está a estrutura de amostra para a definição da classe Apex.

Syntax

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

Esta definição usa uma combinação de modificadores de acesso, modos de compartilhamento, nome da classe e corpo da classe. Veremos todas essas opções mais adiante.

Example

A seguir está um exemplo de estrutura para a definição da classe 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;
   }
}

Modificadores de acesso

Privado

Se você declarar o modificador de acesso como 'Privado', essa classe será conhecida apenas localmente e você não poderá acessar essa classe fora dessa parte específica. Por padrão, as classes têm este modificador.

Público

Se você declarar a classe como 'Pública', isso implica que essa classe está acessível para sua organização e seu namespace definido. Normalmente, a maioria das classes do Apex são definidas com esta palavra-chave.

Global

Se você declarar a classe como 'global', ela poderá ser acessada por todos os códigos apex, independentemente de sua organização. Se você tiver um método definido com a palavra-chave do serviço da web, deverá declarar a classe que o contém com a palavra-chave global.

Modos de Compartilhamento

Vamos agora discutir os diferentes modos de compartilhamento.

Com Compartilhamento

Este é um recurso especial das classes Apex no Salesforce. Quando uma classe é especificada com a palavra-chave 'Com compartilhamento', as seguintes implicações: Quando a classe for executada, ela respeitará as configurações de acesso do usuário e a permissão do perfil. Suponha que a ação do usuário tenha acionado a atualização do registro para 30 registros, mas o usuário tem acesso a apenas 20 registros e 10 registros não estão acessíveis. Assim, se a turma estiver realizando a ação de atualização dos registros, serão atualizados apenas 20 registros aos quais o usuário tem acesso e o restante dos 10 registros não serão atualizados. Isso também é chamado de modo de usuário.

Sem compartilhar

Mesmo que o usuário não tenha acesso a 10 registros em 30, todos os 30 registros serão atualizados à medida que a classe estiver rodando no modo Sistema, ou seja, foi definida com a palavra-chave Sem Compartilhamento. Isso é chamado de modo de sistema.

Virtual

Se você usar a palavra-chave 'virtual', isso indica que esta classe pode ser estendida e substituições são permitidas. Se os métodos precisarem ser substituídos, as classes devem ser declaradas com a palavra-chave virtual.

Abstrato

Se você declarar a classe como 'abstrata', ela conterá apenas a assinatura do método e não a implementação real.

Variáveis ​​de classe

Syntax

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

Na sintaxe acima -

  • O tipo de dados da variável e o nome da variável são obrigatórios
  • Os modificadores de acesso e o valor são opcionais.

Example

public static final Integer myvalue;

Métodos de aula

Existem dois modificadores para Métodos de Classe no Apex - Público ou Protegido. O tipo de retorno é obrigatório para o método e se o método não retornar nada, você deve mencionar void como o tipo de retorno. Além disso, o corpo também é necessário para o método.

Syntax

[public | private | protected | global]
[override]
[static]

return_data_type method_name (input parameters) {
   // Method body goes here
}

Explicação da sintaxe

Esses parâmetros mencionados entre colchetes são opcionais. No entanto, os seguintes componentes são essenciais -

  • return_data_type
  • method_name

Modificadores de acesso para métodos de classe

Usando modificadores de acesso, você pode especificar o nível de acesso para os métodos de classe. Por exemplo, o método Public estará acessível de qualquer lugar na classe e fora da classe. O método privado estará acessível apenas dentro da classe. Global será acessível por todas as classes Apex e pode ser exposto como método de serviço da web acessível por outras classes Apex.

Example

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

Este método tem o tipo de retorno Integer e não leva nenhum parâmetro.

Um método pode ter parâmetros conforme mostrado no exemplo a seguir -

// 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;
}

Construtores de classe

Um construtor é um código que é chamado quando um objeto é criado a partir do blueprint da classe. Ele tem o mesmo nome da classe.

Não precisamos definir o construtor para cada classe, pois por padrão um construtor sem argumento é chamado. Construtores são úteis para inicialização de variáveis ​​ou quando um processo deve ser executado no momento da inicialização da classe. Por exemplo, você gostaria de atribuir valores a certas variáveis ​​inteiras como 0 quando a classe for chamada.

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

Você também pode chamar o método da classe por meio do construtor. Isso pode ser útil ao programar o Apex para o controlador de força visual. Quando o objeto de classe é criado, o construtor é chamado conforme mostrado abaixo -

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

Sobrecarregando Construtores

Os construtores podem estar sobrecarregados, ou seja, uma classe pode ter mais de um construtor definido com parâmetros diferentes.

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

Você pode executar esta classe como fizemos no exemplo anterior.

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

Uma instância de classe é chamada de Object. Em termos de Salesforce, o objeto pode ser de classe ou você pode criar um objeto de sObject também.

Criação de objeto da classe

Você pode criar um objeto de classe como faria em Java ou outra linguagem de programação orientada a objetos.

A seguir está um exemplo de classe chamada 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);
   }
}

Esta é uma classe de instância, ou seja, para chamar ou acessar as variáveis ​​ou métodos desta classe, você deve criar uma instância desta classe e então você pode realizar todas as operações.

// Object Creation
// Creating an object of class
MyClass objClass = new MyClass();

// Calling Class method using Class instance
objClass.myMethod(100);

Criação de objetos

sObjects são os objetos do Salesforce nos quais você armazena os dados. Por exemplo, Conta, Contato, etc., são objetos personalizados. Você pode criar instâncias de objeto desses sObjects.

A seguir está um exemplo de inicialização de sObject e mostra como você pode acessar o campo daquele objeto específico usando a notação de ponto e atribuir os valores aos campos.

// 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);

Inicialização estática

Variáveis ​​e métodos estáticos são inicializados apenas uma vez quando uma classe é carregada. Variáveis ​​estáticas não são transmitidas como parte do estado de exibição de uma página do Visualforce.

A seguir está um exemplo de método estático, bem como variável estática.

// 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

Variáveis ​​estáticas serão instanciadas apenas uma vez quando a classe for carregada e este fenômeno pode ser usado para evitar a recursão do trigger. O valor da variável estática será o mesmo dentro do mesmo contexto de execução e qualquer classe, gatilho ou código em execução pode se referir a ele e evitar a recursão.

Uma interface é como uma classe Apex na qual nenhum dos métodos foi implementado. Ele contém apenas as assinaturas do método, mas o corpo de cada método está vazio. Para usar uma interface, outra classe deve implementá-la fornecendo um corpo para todos os métodos contidos na interface.

As interfaces são usadas principalmente para fornecer a camada de abstração para o seu código. Eles separam a implementação da declaração do método.

Vamos dar um exemplo de nossa empresa química. Suponha que precisemos fornecer o desconto para clientes Premium e Ordinários e os descontos para ambos serão diferentes.

Vamos criar uma interface chamada de 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;
   }
}

Quando você implementa a Interface, é obrigatório implementar o método dessa Interface. Se você não implementar os métodos de interface, ocorrerá um erro. Você deve usar Interfaces quando quiser tornar a implementação do método obrigatória para o desenvolvedor.

Interface Salesforce padrão para Batch Apex

SFDC tem interfaces padrão como Database.Batchable, Schedulable, etc. Por exemplo, se você implementar a Interface Database.Batchable, então você deve implementar os três métodos definidos na Interface - Iniciar, Executar e Concluir.

Abaixo está um exemplo de Database.Batchable Interface fornecido pelo Salesforce padrão, que envia e-mails para usuários com o Status do lote. Esta interface possui 3 métodos: Iniciar, Executar e Concluir. Usando esta interface, podemos implementar a funcionalidade Batchable e também fornece a variável BatchableContext que podemos usar para obter mais informações sobre o Batch que está em execução e para realizar outras funcionalidades.

global class CustomerProessingBatch implements Database.Batchable<sobject7>,
Schedulable {
   // Add here your email address
   global String [] email = new String[] {'[email protected]'};

   // 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('[email protected]');
      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);
   }
}

Para executar esta classe, você deve executar o código abaixo no console do desenvolvedor.

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

Neste capítulo, discutiremos como executar as diferentes funcionalidades de modificação do banco de dados no Salesforce. Existem dois comandos com os quais podemos realizar as funcionalidades.

Declarações DML

DML são as ações executadas para realizar a operação de inserção, atualização, exclusão, upsert, restauração de registros, fusão de registros ou conversão de leads.

DML é uma das partes mais importantes no Apex, pois quase todo business case envolve as mudanças e modificações no banco de dados.

Métodos de banco de dados

Todas as operações que você pode executar usando instruções DML podem ser executadas usando métodos de banco de dados também. Métodos de banco de dados são os métodos do sistema que você pode usar para realizar operações DML. Os métodos de banco de dados fornecem mais flexibilidade em comparação com as instruções DML.

Neste capítulo, examinaremos a primeira abordagem usando DML Statements. Veremos os métodos de banco de dados em um capítulo subsequente.

Declarações DML

Vamos agora considerar o caso da empresa fornecedora de produtos químicos novamente. Nossos registros de fatura têm campos como Status, Valor pago, Valor restante, Próxima data de pagamento e Número da fatura. As faturas que foram criadas hoje e estão com o status 'Pendente', devem ser atualizadas para 'Pagas'.

Operação de inserção

A operação de inserção é usada para criar novos registros no banco de dados. Você pode criar registros de qualquer objeto padrão ou personalizado usando a instrução Insert DML.

Example

Podemos criar novos registros no objeto APEX_Invoice__c à medida que novas faturas são geradas para novos pedidos de clientes todos os dias. Criaremos um registro de cliente primeiro e, em seguida, podemos criar um registro de fatura para esse novo registro de cliente.

// 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);

Operação de atualização

A operação de atualização consiste em realizar atualizações nos registros existentes. Neste exemplo, estaremos atualizando o campo Status de um registro de fatura existente para 'Pago'.

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]);

Operação Upsert

A operação Upsert é usada para realizar uma operação de atualização e se os registros a serem atualizados não estiverem presentes no banco de dados, então crie novos registros também.

Example

Suponha que os registros do cliente no objeto Cliente precisem ser atualizados. Atualizaremos o registro do cliente existente se ele já estiver presente, caso contrário, criaremos um novo. Isso será baseado no valor do campo APEX_External_Id__c. Este campo será o nosso campo para identificar se os registros já estão presentes ou não.

Note - Antes de executar este código, crie um registro no objeto Cliente com o valor do campo de ID externo como '12341' e execute o código fornecido abaixo -

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

Excluir operação

Você pode realizar a operação de exclusão usando Delete DML.

Example

Neste caso, iremos eliminar as faturas que foram criadas para efeitos de teste, ou seja, aquelas que contêm o nome como 'Teste'.

Você também pode executar este snippet no console do desenvolvedor sem criar a classe.

// 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');

Operação de undelete

Você pode recuperar o registro que foi excluído e está presente na Lixeira. Todos os relacionamentos que o registro excluído possui, também serão restaurados.

Example

Suponha que os registros excluídos no exemplo anterior precisem ser restaurados. Isso pode ser feito usando o exemplo a seguir. O código do exemplo anterior foi modificado para este exemplo.

// 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');

Os métodos de classe de banco de dados são outra maneira de trabalhar com instruções DML, que são mais flexíveis do que as instruções DML, como inserir, atualizar, etc.

Diferenças entre métodos de banco de dados e instruções DML

Declarações DML Métodos de banco de dados
A atualização parcial não é permitida. Por exemplo, se você tiver 20 registros na lista, todos os registros serão atualizados ou nenhum. A atualização parcial é permitida. Você pode especificar o método Parâmetro no Banco de Dados como verdadeiro ou falso, verdadeiro para permitir a atualização parcial e falso para não permitir a mesma.
Você não pode obter a lista de registros de sucesso e falha. Você pode obter a lista de registros de sucesso e falha como vimos no exemplo.
Example - inserir listName Example - Database.insert (listName, False), onde false indica que a atualização parcial não é permitida.

Operação de inserção

A inserção de novos registros por meio de métodos de banco de dados também é bastante simples e flexível. Vamos considerar o cenário anterior em que inserimos novos registros usando as instruções DML. Estaremos inserindo o mesmo usando métodos de banco de dados.

Exemplo

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

Operação de atualização

Vamos agora considerar nosso exemplo de caso de negócios usando os métodos de banco de dados. Suponha que precisemos atualizar o campo de status do objeto Invoice, mas, ao mesmo tempo, também exigimos informações como status de registros, ids de registro com falha, contagem de sucesso, etc. Isso não é possível usando declarações DML, portanto, devemos usar métodos de banco de dados para obter o status de nossa operação.

Exemplo

Estaremos atualizando o campo 'Status' da Fatura se ela estiver com o status 'Pendente' e a data de criação for hoje.

O código fornecido a seguir ajudará na atualização dos registros de fatura usando o método Database.update. Além disso, crie um registro de fatura antes de executar este código.

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

Estaremos examinando apenas as operações de Inserir e Atualizar neste tutorial. As outras operações são bastante semelhantes a essas operações e ao que fizemos no capítulo anterior.

Todo negócio ou aplicativo tem a funcionalidade de pesquisa como um dos requisitos básicos. Para isso, a Salesforce.com oferece duas abordagens principais usando SOSL e SOQL. Vamos discutir a abordagem SOSL em detalhes neste capítulo.

SOSL

A pesquisa da string de texto no objeto e no campo será feita usando SOSL. Esta é a linguagem de pesquisa de objetos do Salesforce. Ele tem a capacidade de pesquisar uma string específica em vários objetos.

As instruções SOSL são avaliadas como uma lista de sObjects, em que cada lista contém os resultados da pesquisa para um determinado tipo de sObject. As listas de resultados são sempre retornadas na mesma ordem em que foram especificadas na consulta SOSL.

Exemplo de consulta SOSL

Considere um caso de negócios em que precisamos desenvolver um programa que possa pesquisar uma string especificada. Suponha que precisamos pesquisar a string 'ABC' no campo Nome do cliente do objeto Fatura. O código é o seguinte -

Primeiro, você deve criar um único registro no objeto Fatura com o nome do cliente como 'ABC' para que possamos obter um resultado válido quando pesquisado.

// 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

Isso é quase o mesmo que SOQL. Você pode usar isso para buscar os registros do objeto de um objeto apenas por vez. Você pode escrever consultas aninhadas e também buscar os registros do objeto pai ou filho no qual você está consultando agora.

Exploraremos SOQL no próximo capítulo.

Esta é a linguagem de consulta de objeto do Salesforce projetada para funcionar com o banco de dados SFDC. Ele pode pesquisar um registro em um determinado critério apenas em um único sObject.

Como o SOSL, ele não pode pesquisar em vários objetos, mas oferece suporte a consultas aninhadas.

Exemplo SOQL

Considere nosso exemplo contínuo de Chemical Company. Suponha que precisamos de uma lista de registros que são criados hoje e cujo nome de cliente não é 'teste'. Neste caso, teremos que usar a consulta SOQL conforme fornecido abaixo -

// 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
}

Você pode executar a consulta SOQL por meio do Editor de Consultas no console do desenvolvedor, conforme mostrado abaixo.

Execute a consulta fornecida abaixo no console do desenvolvedor. Pesquise os registros de fatura criados hoje.

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

Você deve selecionar os campos para os quais precisa dos valores, caso contrário, pode gerar erros de tempo de execução.

Atravessando campos de relacionamento

Esta é uma das partes mais importantes no SFDC, tantas vezes que precisamos atravessar o relacionamento de objeto pai-filho

Além disso, pode haver casos em que você precise inserir dois registros de objetos associados no Banco de Dados. Por exemplo, o objeto Fatura tem relacionamento com o objeto Cliente e, portanto, um Cliente pode ter várias faturas.

Suponha que você esteja criando a fatura e, em seguida, precise relacionar esta fatura ao Cliente. Você pode usar o seguinte código para esta funcionalidade -

// 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

Execute este trecho de código no console do desenvolvedor. Uma vez executado, copie o Id da fatura do console do desenvolvedor e abra o mesmo no SFDC conforme mostrado abaixo. Você pode ver que o registro principal já foi atribuído ao registro de fatura, conforme mostrado abaixo.

Buscando Registros Filhos

Vamos agora considerar um exemplo em que todas as faturas relacionadas a um determinado registro do cliente precisam estar em um só lugar. Para isso, você deve saber o nome do relacionamento do filho. Para ver o nome do relacionamento filho, acesse a página de detalhes do campo no objeto filho e verifique o valor "Relacionamento filho". Em nosso exemplo, são faturas anexadas por __r no final.

Exemplo

Neste exemplo, precisaremos configurar os dados, criar um cliente com o nome de registro 'Cliente ABC' e adicionar 3 faturas a esse cliente.

Agora, vamos buscar as faturas que o cliente 'Cliente ABC' possui. A seguir está a consulta para o mesmo -

// 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

Você pode ver os valores de registro nos logs de depuração.

Buscando registro pai

Suponha que você precise buscar o Nome do Cliente da Fatura, cuja data de criação é hoje, então você pode usar a consulta fornecida abaixo para a mesma -

Exemplo

Busque o valor do registro pai junto com o objeto filho.

// 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
}

Aqui, usamos a notação APEX_Customer__r.Name, onde APEX_Customer__r é o nome do relacionamento pai, aqui você deve anexar __r no final do campo Pai e, em seguida, pode buscar o valor do campo pai.

Funções de agregação

SOQL tem função agregada como temos em SQL. As funções de agregação nos permitem acumular e resumir os dados. Vamos agora entender a função em detalhes.

Suponha que você queira saber qual é a receita média que estamos obtendo do cliente 'Cliente ABC', então você pode usar esta função para obter a média.

Exemplo

// 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);

Verifique a saída nos logs de depuração. Observe que qualquer consulta que inclui uma função agregada retorna seus resultados em uma matriz deAggregateResultobjetos. AggregateResult é um sObject somente leitura e só é usado para resultados de consulta. É útil quando precisamos gerar o Relatório de dados grandes.

Existem também outras funções de agregação que você pode usar para realizar o resumo dos dados.

MIN() - Isso pode ser usado para encontrar o valor mínimo

MAX() - Isso pode ser usado para encontrar o valor máximo.

Variáveis ​​de ligação do Apex

Você pode usar a variável Apex na consulta SOQL para buscar os resultados desejados. As variáveis ​​Apex podem ser referenciadas pela notação Colon (:).

Exemplo

// 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

A segurança do Apex se refere ao processo de aplicação de configurações de segurança e reforço das regras de compartilhamento no código em execução. As classes Apex têm configurações de segurança que podem ser controladas por meio de duas palavras-chave.

Regras de segurança e compartilhamento de dados

O Apex geralmente é executado no contexto do sistema, ou seja, nas permissões do usuário atual. A segurança em nível de campo e as regras de compartilhamento não são levadas em consideração durante a execução do código. Apenas o código de bloco anônimo é executado com a permissão do usuário que está executando o código.

Nosso código Apex não deve expor ao usuário os dados confidenciais que estão ocultos por meio das configurações de segurança e compartilhamento. Portanto, a segurança do Apex e o cumprimento da regra de compartilhamento são muito importantes.

Com palavras-chave de compartilhamento

Se você usar esta palavra-chave, o código do Apex irá forçar as configurações de compartilhamento do usuário atual para o código do Apex. Isso não impõe a permissão de Perfil, apenas as configurações de compartilhamento de nível de dados.

Vamos considerar um exemplo em que, nosso usuário tem acesso a 5 registros, mas o número total de registros é 10. Portanto, quando a classe Apex for declarada com a palavra-chave "Com compartilhamento", ela retornará apenas 5 registros nos quais o usuário tem acesso a.

Example

Primeiro, certifique-se de ter criado pelo menos 10 registros no objeto Cliente com 'Nome' de 5 registros como 'Cliente ABC' e 5 registros restantes como 'Cliente XYZ'. Em seguida, crie uma regra de compartilhamento que compartilhará o 'Cliente ABC' com todos os usuários. Também precisamos ter certeza de que definimos o OWD do objeto Cliente como Privado.

Cole o código fornecido abaixo no bloco Anônimo no console do desenvolvedor.

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

Sem compartilhar palavras-chave

Como o nome sugere, a classe declarada com esta palavra-chave é executada no modo Sistema, ou seja, independente do acesso do usuário ao registro, a consulta irá buscar todos os registros.

// 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.

Configurando Segurança para Apex Class

Você pode ativar ou desativar uma classe Apex para um perfil específico. As etapas para o mesmo são fornecidas abaixo. Você pode determinar qual perfil deve ter acesso a qual classe.

Configurando a segurança da classe Apex na página de lista de classes

Step 1 - Em Configuração, clique em Desenvolver → Classes Apex.

Step 2- Clique no nome da turma que você deseja restringir. Clicamos em CustomerOperationClass.

Step 3 - Clique em Segurança.

Step 4 - Selecione os perfis que deseja ativar na lista Perfis disponíveis e clique em Adicionar, ou selecione os perfis que deseja desativar na lista Perfis ativados e clique em Remover.

Step 5 - Clique em Salvar.

Configurando Segurança Apex a partir de Conjunto de Permissão

Step 1 - Em Configuração, clique em Gerenciar usuários → Conjuntos de permissões.

Step 2 - Selecione um conjunto de permissões.

Step 3 - Clique em Acesso à classe Apex.

Step 4 - Clique em Editar.

Step 5 - Selecione as classes Apex que deseja habilitar na lista Classes Apex Disponíveis e clique em Adicionar ou selecione as classes Apex que deseja desabilitar na lista Classes Apex Habilitadas e clique em remover.

Step 6 - Clique no botão Salvar.

A chamada do Apex se refere ao processo de execução da classe Apex. A classe Apex só pode ser executada quando é invocada por uma das maneiras listadas abaixo -

  • Gatilhos e bloqueio anônimo

  • Um gatilho invocado para eventos específicos

  • Apex Assíncrono

  • Agendar uma classe Apex para ser executada em intervalos especificados ou executar um trabalho em lote

  • Aula de serviços web

  • Classe Apex Email Service

  • Apex Web Services, que permite expor seus métodos via serviços da Web SOAP e REST

  • Controladores Visualforce

  • Apex Email Service para processar e-mail de entrada

  • Invocando Apex usando JavaScript

  • O kit de ferramentas Ajax para invocar métodos de serviço da Web implementados no Apex

Agora vamos entender algumas maneiras comuns de invocar o Apex.

De Executar Bloco Anônimo

Você pode invocar a classe Apex por meio da execução anônima no console do desenvolvedor, conforme mostrado abaixo -

Step 1 - Abra o console do desenvolvedor.

Step 2 - Clique em Debug.

Step 3- A janela Executar anônimo será aberta conforme mostrado abaixo. Agora, clique no botão Executar -

Step 4 - Abra o registro de depuração quando ele aparecerá no painel de registros.

De Trigger

Você também pode chamar uma classe Apex a partir do Trigger. Os gatilhos são chamados quando um evento especificado ocorre e os gatilhos podem chamar a classe Apex durante a execução.

A seguir está o código de exemplo que mostra como uma classe é executada quando um Trigger é chamado.

Exemplo

// 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.

Do código do controlador de página do Visualforce

A classe Apex também pode ser chamada na página do Visualforce. Podemos especificar o controlador ou a extensão do controlador e a classe Apex especificada é chamada.

Exemplo

VF Page Code

Apex Class Code (Controller Extension)

Os gatilhos do Apex são como procedimentos armazenados que são executados quando ocorre um determinado evento. Um gatilho é executado antes e depois da ocorrência de um evento no registro.

Sintaxe

trigger triggerName on ObjectName (trigger_events) { Trigger_code_block }

Executando o gatilho

A seguir estão os eventos em que podemos disparar o gatilho -

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

Exemplo 1 de gatilho

Suponha que recebemos um requisito de negócios de que precisamos criar um Registro de Fatura quando o campo 'Status do Cliente' do Cliente muda para Ativo de Inativo. Para isso, criaremos um gatilho no objeto APEX_Customer__c seguindo estas etapas -

Step 1 - Vá para sObject

Step 2 - Clique em Cliente

Step 3 - Clique no botão 'Novo' na lista relacionada do acionador e adicione o código do acionador conforme abaixo.

// 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;
}

Explicação

Trigger.new- Esta é a variável de contexto que armazena os registros atualmente no contexto do acionador, sendo inseridos ou atualizados. Neste caso, esta variável contém os registros do objeto Cliente que foram atualizados.

Existem outras variáveis ​​de contexto que estão disponíveis no contexto - trigger.old, trigger.newMap, trigger.OldMap.

Exemplo de gatilho 2

O gatilho acima será executado quando houver uma operação de atualização nos registros do Cliente. Suponha que o registro da fatura precise ser inserido apenas quando o Status do cliente mudar de Inativo para Ativo e não sempre; para isso, podemos usar outra variável de contextotrigger.oldMap que armazenará a chave como id de registro e o valor como valores de registro antigos.

// 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;
}

Explicação

Usamos a variável Trigger.oldMap que, conforme explicado anteriormente, é uma variável de contexto que armazena o Id e o valor antigo dos registros que estão sendo atualizados.

Os padrões de design são usados ​​para tornar nosso código mais eficiente e evitar atingir os limites do governador. Freqüentemente, os desenvolvedores podem escrever código ineficiente que pode causar instanciação repetida de objetos. Isso pode resultar em código ineficiente e com desempenho insatisfatório e, potencialmente, na violação dos limites do regulador. Isso ocorre mais comumente em gatilhos, pois eles podem operar em um conjunto de registros.

Veremos algumas estratégias de padrão de projeto importantes neste capítulo.

Bulk Triggers Design Patterns

No caso de negócios reais, será possível que você precise processar milhares de registros de uma vez. Se o seu gatilho não foi projetado para lidar com tais situações, ele pode falhar durante o processamento dos registros. Existem algumas práticas recomendadas que você precisa seguir ao implementar os gatilhos. Todos os gatilhos são em massa por padrão e podem processar vários registros de uma vez. Você deve sempre planejar o processamento de mais de um registro por vez.

Considere um caso de negócios, em que você precisa processar um grande número de registros e escreveu o gatilho conforme fornecido a seguir. Este é o mesmo exemplo que tomamos para inserir o registro da fatura quando o Status do Cliente muda de Inativo para Ativo.

// 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
      }
   }
}

Agora você pode ver que a instrução DML foi escrita para o bloco de loop que funcionará ao processar apenas alguns registros, mas quando você estiver processando algumas centenas de registros, atingirá o limite da instrução DML por transação que é o governor limit. Teremos uma visão detalhada dos limites do governador em um capítulo subsequente.

Para evitar isso, temos que tornar o gatilho eficiente para processar vários registros de uma vez.

O exemplo a seguir ajudará você a entender o mesmo -

// 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
}

Este gatilho disparará apenas 1 instrução DML, pois estará operando sobre uma lista e a lista tem todos os registros que precisam ser modificados.

Dessa forma, você pode evitar os limites do controlador de instrução DML.

Classe Trigger Helper

Escrever todo o código no gatilho também não é uma boa prática. Portanto, você deve chamar a classe Apex e delegar o processamento da classe Trigger para a classe Apex, conforme mostrado abaixo. A classe Trigger Helper é a classe que faz todo o processamento para o gatilho.

Vamos considerar nosso exemplo de criação de registro de fatura novamente.

// 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
}

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 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
   }
}

Nesse caso, todo o processamento foi delegado à classe auxiliar e, quando precisarmos de uma nova funcionalidade, podemos simplesmente adicionar o código à classe auxiliar sem modificar o gatilho.

Gatilho único em cada sObject

Sempre crie um único gatilho em cada objeto. Vários gatilhos no mesmo objeto podem causar o conflito e erros se atingir os limites do governador.

Você pode usar a variável de contexto para chamar os diferentes métodos da classe auxiliar de acordo com o requisito. Considere nosso exemplo anterior. Suponha que nosso método createInvoice deva ser chamado apenas quando o registro é atualizado e em vários eventos. Então, podemos controlar a execução como abaixo -

// 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
   }
}

Os limites de execução do governador garantem o uso eficiente de recursos na plataforma multilocatário Force.com. É o limite especificado pelo Salesforce.com na execução de código para processamento eficiente.

Quais são os limites do governador?

Como sabemos, o Apex funciona em ambiente multi-tenant, ou seja, um único recurso é compartilhado por todos os clientes e organizações. Portanto, é necessário garantir que ninguém monopolize os recursos e, portanto, a Salesforce.com criou o conjunto de limites que rege e limita a execução do código. Sempre que qualquer um dos limites do regulador for ultrapassado, ocorrerá um erro e interromperá a execução do programa.

Do ponto de vista do desenvolvedor, é importante garantir que nosso código seja escalonável e não atinja os limites.

Todos esses limites são aplicados por transação. Uma única execução de gatilho é uma transação.

Como vimos, o padrão de design do acionador ajuda a evitar o erro de limite. Veremos agora outros limites importantes.

Evitando o limite de consulta SOQL

Você pode emitir apenas 100 consultas por transação, ou seja, quando seu código emitir mais de 100 consultas SOQL, ele gerará um erro.

Exemplo

Este exemplo mostra como o limite da consulta SOQL pode ser alcançado -

O gatilho a seguir itera sobre uma lista de clientes e atualiza a descrição do registro filho (fatura) com a string 'Ok para pagar'.

// 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
         }
      }
   }
}

Quando o método 'updateCustomerDescription' é chamado e o número de registros do cliente é superior a 100, ele atinge o limite de SOQL. Para evitar isso, nunca escreva a consulta SOQL no For Loop. Nesse caso, a consulta SOQL foi escrita no loop For.

A seguir está um exemplo que mostrará como evitar o limite de DML e também de SOQL. Usamos a consulta de relacionamento aninhado para buscar os registros da fatura e usamos a variável de contextotrigger.newMap para obter o mapa de id e registros do cliente.

// 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;
   }
}

Chamadas DML em massa

Este exemplo mostra o gatilho Bulk junto com o padrão de classe auxiliar do gatilho. Você deve salvar a classe auxiliar primeiro e, em seguida, salvar o gatilho.

Note - Cole o código abaixo na classe 'CustomerTriggerHelper' que criamos anteriormente.

// 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
}

Outros limites do governador do Salesforce

A tabela a seguir lista os limites importantes do regulador.

Descrição Limite
Tamanho total da pilha 6 MB / 12 MB
Número total de declarações DML emitidas 150
Número total de registros recuperados por uma única consulta SOSL 2000
Número total de consultas SOSL emitidas 20
Número total de registros recuperados por Database.getQueryLocator 10.000
Número total de registros recuperados por consultas SOQL 50000

Neste capítulo, entenderemos o processamento em lote no Apex. Considere um cenário em que processaremos um grande número de registros diariamente, provavelmente a limpeza de dados ou talvez a exclusão de alguns dados não utilizados.

O que é o Batch Apex?

O Apex em lote é a execução assíncrona do código Apex, especialmente projetado para processar o grande número de registros e tem maior flexibilidade nos limites do governador do que o código síncrono.

Quando usar o Batch Apex?

  • Quando você deseja processar um grande número de registros diariamente ou mesmo em um intervalo de tempo específico, você pode ir para o Batch Apex.

  • Além disso, quando você deseja que uma operação seja assíncrona, você pode implementar o Apex em lote. O Batch Apex é exposto como uma interface que deve ser implementada pelo desenvolvedor. Os trabalhos em lote podem ser chamados programaticamente no tempo de execução usando o Apex. O Batch Apex opera em pequenos lotes de registros, cobrindo todo o seu conjunto de registros e dividindo o processamento em blocos de dados gerenciáveis.

Usando Batch Apex

Quando estamos usando o Batch Apex, devemos implementar a interface Database.Batchable fornecida pelo Salesforce e, em seguida, invocar a classe programaticamente.

Você pode monitorar a aula seguindo estas etapas -

Para monitorar ou interromper a execução do trabalho em lote do Apex Batch, vá para Configuração → Monitoramento → Trabalhos do Apex ou Trabalhos → Trabalhos do Apex.

A interface Database.Batchable possui os três métodos a seguir que precisam ser implementados -

  • Start
  • Execute
  • Finish

Vamos agora entender cada método em detalhes.

Começar

O método Start é um dos três métodos da interface Database.Batchable.

Syntax

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

Este método será chamado no início do Batch Job e coleta os dados nos quais o Batch Job estará operando.

Considere os seguintes pontos para entender o método -

  • Use o Database.QueryLocatorobjeto quando você está usando uma consulta simples para gerar o escopo dos objetos usados ​​no trabalho em lote. Nesse caso, o limite de linha de dados SOQL será ignorado.

  • Use o objeto iterável quando tiver critérios complexos para processar os registros. Database.QueryLocator determina o escopo dos registros que devem ser processados.

Executar

Vamos agora entender o método Execute da interface Database.Batchable.

Syntax

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

onde, list <sObject <é retornado pelo método Database.QueryLocator.

Este método é chamado após o método Start e faz todo o processamento necessário para o Batch Job.

Terminar

Vamos agora discutir o método Finish da interface Database.Batchable.

Syntax

global void finish(Database.BatchableContext BC) {}

Este método é chamado no final e você pode realizar algumas atividades de finalização, como enviar um e-mail com informações sobre os registros e status do trabalho em lote processado.

Exemplo de Apex em lote

Vamos considerar um exemplo de nossa Empresa Química existente e assumir que temos a necessidade de atualizar o campo Status do Cliente e Descrição do Cliente dos Registros do Cliente que foram marcados como Ativos e que criaram a Data como hoje. Isso deve ser feito diariamente e um e-mail deve ser enviado a um usuário sobre o status do processamento em lote. Atualize o status do cliente como 'Processado' e a descrição do cliente como 'Atualizado por meio de trabalho em lote'.

// Batch Job for Processing the Records
global class CustomerProessingBatch implements Database.Batchable<sobject> {
   global String [] email = new String[] {'[email protected]'};
   // 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('[email protected]'); // 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});
   }
}

Para executar este código, primeiro salve-o e cole o código a seguir em Executar anônimo. Isso criará o objeto da classe e o método Database.execute executará o trabalho Batch. Assim que o trabalho for concluído, um e-mail será enviado para o endereço de e-mail especificado. Certifique-se de ter um registro de cliente que tenhaActive conforme verificado.

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

Assim que esta aula for executada, verifique o endereço de e-mail que você forneceu, onde receberá o e-mail com as informações. Além disso, você pode verificar o status da tarefa em lote por meio da página Monitoramento e das etapas fornecidas acima.

Se você verificar os logs de depuração, poderá encontrar o tamanho da lista, que indica quantos registros foram processados.

Limitations

Podemos ter apenas 5 processamento de trabalhos em lote de cada vez. Esta é uma das limitações do Batch Apex.

Programando o Apex Batch Job usando a página de detalhes do Apex

Você pode agendar a classe Apex por meio da página de detalhes do Apex conforme fornecido abaixo -

Step 1 - Vá para Configuração ⇒ Classes Apex, clique em Classes Apex.

Step 2 - Clique no botão Agendar Apex.

Step 3 - Forneça detalhes.

Agendando o Apex Batch Job usando a Interface Agendável

Você pode agendar o Apex Batch Job usando a Interface Agendável conforme fornecido abaixo -

// Batch Job for Processing the Records
global class CustomerProessingBatch implements Database.Batchable<sobject> {
   global String [] email = new String[] {'[email protected]'};
   // 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('[email protected]');//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);

A depuração é uma parte importante em qualquer desenvolvimento de programação. No Apex, temos certas ferramentas que podem ser usadas para depuração. Um deles é o método system.debug () que imprime o valor e a saída da variável nos logs de depuração.

Podemos usar as duas ferramentas a seguir para depuração -

  • Console do desenvolvedor
  • Registros de depuração

Depuração via console do desenvolvedor

Você pode usar o console do desenvolvedor e executar a funcionalidade anônima para depurar o Apex conforme abaixo -

Example

Considere nosso exemplo existente de buscar os registros do cliente que foram criados hoje. Queremos apenas saber se a consulta está retornando os resultados ou não e se sim, verificaremos o valor de List.

Cole o código fornecido abaixo em executar janela anônima e siga os passos que fizemos para abrir a janela executar anônima.

Step 1 - Abra o console do desenvolvedor

Step 2 - Abra Executar anônimo em 'Depurar', conforme mostrado abaixo.

Step 3 - Abra a janela Executar anônimo, cole o seguinte código e clique em executar.

// 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 - Abra os Logs conforme mostrado abaixo.

Step 5 - Insira 'USUÁRIO' na condição de filtro conforme mostrado abaixo.

Step 6 - Abra a instrução USER DEBUG conforme mostrado abaixo.

Depuração por meio de registros de depuração

Você também pode depurar a mesma classe por meio de logs de depuração. Suponha que você tenha um gatilho no objeto Cliente e precise ser depurado para alguns valores de variáveis, então você pode fazer isso por meio dos logs de depuração, conforme mostrado abaixo -

Este é o Código de Disparo que atualiza o campo Descrição se o cliente modificado estiver ativo e você quiser verificar os valores das variáveis ​​e registros atualmente no escopo -

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

Siga as etapas fornecidas abaixo para gerar os logs de depuração.

Step 1- Defina os logs de depuração para seu usuário. Vá para Configuração e digite 'Registro de depuração' na janela de configuração de pesquisa e clique em Link.

Step 2 - Defina os logs de depuração conforme a seguir.

Step 3- Insira o nome do usuário que requer configuração. Escreva o seu nome aqui.

Step 4 - Modifique os registros do cliente conforme o evento deve ocorrer para gerar o log de depuração.

Step 5- Agora vá para a seção de logs de depuração novamente. Abra os logs de depuração e clique em Reset.

Step 6 - Clique no link visualizar do primeiro log de depuração.

Step 7 - Pesquise a string 'USER' usando a pesquisa do navegador conforme mostrado abaixo.

A instrução de depuração mostrará o valor do campo no qual definimos o ponto.

O teste é parte integrante do Apex ou de qualquer outro desenvolvimento de aplicativo. No Apex, temos classes de teste separadas para desenvolver para todos os testes de unidade.

Classes de Teste

No SFDC, o código deve ter 75% de cobertura de código para ser implantado na produção. Essa cobertura de código é realizada pelas classes de teste. As classes de teste são fragmentos de código que testam a funcionalidade de outra classe do Apex.

Vamos escrever uma classe de teste para um de nossos códigos que escrevemos anteriormente. Vamos escrever uma classe de teste para cobrir nosso código de classe Trigger e Helper. Abaixo está a classe de gatilho e auxiliar que precisa ser coberta.

// 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
   }
}

Criando classe de teste

Nesta seção, vamos entender como criar uma classe de teste.

Criação de Dados

Precisamos criar dados para a classe de teste em nossa própria classe de teste. A classe de teste, por padrão, não tem acesso aos dados da organização, mas se você definir @isTest (seeAllData = true), ela também terá acesso aos dados da organização.

anotação @isTest

Ao usar essa anotação, você declarou que esta é uma classe de teste e não será contabilizada no limite total de código da organização.

palavra-chave testMethod

Os métodos de teste de unidade são os métodos que não aceitam argumentos, não confirmam dados no banco de dados, não enviam e-mails e são declarados com a palavra-chave testMethod ou a anotação isTest na definição do método. Além disso, os métodos de teste devem ser definidos em classes de teste, ou seja, classes anotadas com isTest.

Usamos o método de teste 'myUnitTest' em nossos exemplos.

Test.startTest () e Test.stopTest ()

Esses são os métodos de teste padrão que estão disponíveis para as aulas de teste. Esses métodos contêm o evento ou ação para o qual estaremos simulando nosso teste. Como neste exemplo, testaremos nossa classe de gatilho e auxiliar para simular o gatilho de incêndio atualizando os registros como fizemos para iniciar e parar o bloco. Isso também fornece um limite de governador separado para o código que está no bloco de início e parada.

System.assert ()

Este método verifica a saída desejada com o real. Neste caso, esperamos que um registro de fatura seja inserido, então adicionamos assert para verificar o mesmo.

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
   }
}

Executando a classe de teste

Siga as etapas fornecidas abaixo para executar a classe de teste -

Step 1 - Vá para as classes do Apex ⇒ clique no nome da classe 'CustomerTriggerTestClass'.

Step 2 - Clique no botão Executar teste, conforme mostrado.

Step 3 - Verifique o status

Step 4 - Agora verifique a classe e o gatilho para o qual escrevemos o teste

Classe

Desencadear

Nosso teste foi concluído com sucesso.

O que é implantação no SFDC?

Até agora, desenvolvemos o código no Developer Edition, mas no cenário da vida real, você precisa fazer esse desenvolvimento no Sandbox e, em seguida, pode precisar implantá-lo em outro sandbox ou ambiente de produção e isso é chamado de implantação. Resumindo, é o movimento de metadados de uma organização para outra. A razão por trás disso é que você não pode desenvolver o Apex em sua organização de produção do Salesforce. Usuários ativos que acessam o sistema durante o desenvolvimento podem desestabilizar seus dados ou corromper seu aplicativo.

Ferramentas disponíveis para implantação -

  • IDE Force.com
  • Conjuntos de mudança
  • API SOAP
  • Ferramenta de migração Force.com

Como estamos usando a Developer Edition para nosso propósito de desenvolvimento e aprendizado, não podemos usar o Change Set ou outras ferramentas que precisam do SFDC enterprise ou outra edição paga. Portanto, estaremos elaborando o método de implantação do IDE Force.com neste tutorial.

IDE Eclipse da Force.com

Step 1 - Abra o Eclipse e abra o gatilho de classe que precisa ser implementado.

Step 2 - Depois de clicar em 'Implementar no servidor', insira o nome de usuário e a senha da organização em que o componente precisa ser implementado.

Ao realizar as etapas mencionadas acima, seus componentes do Apex serão implantados na organização de destino.

Implantação usando conjunto de mudanças

Você pode implantar regras de validação, regras de fluxo de trabalho, classes Apex e Trigger de uma organização para outra, conectando-os por meio das configurações de implantação. Nesse caso, as organizações devem estar conectadas.

Para abrir a configuração de implantação, siga as etapas abaixo. Lembre-se de que este recurso não está disponível na Developer Edition -

Step 1 - Vá para Configuração e pesquise 'Implementar'.

Step 2 - Clique em 'Outbound Change Set' para criar um conjunto de mudanças para implantar.

Step 3 - Adicione componentes para alterar o conjunto usando o botão 'Adicionar' e, em seguida, Salvar e clique em Carregar.

Step 4 - Vá para a organização de destino e clique no conjunto de mudanças de entrada e, finalmente, clique em implantar.

Chamadas de API SOAP para implantação

Teremos apenas uma pequena visão geral desse método, pois não é um método comumente usado.

Você pode usar as chamadas de método fornecidas abaixo para implantar seus metadados.

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

Ferramenta de migração Force.com

Esta ferramenta é usada para a implantação com script. Você precisa baixar a ferramenta de migração Force.com e, em seguida, pode executar a implantação baseada em arquivo. Você pode baixar a ferramenta de migração Force.com e, em seguida, fazer a implantação com script.