img1

Preface

Checkout the following articles before tackling this one:

  1. Basics and Definitions
  2. Future methods

Learning Objectives

After completing this article, you’ll be able to describe:

Prologue

Batch Apex is used to run large number of jobs asynchronously. There are processed in batches, hence the name. You generally use Batch Apex when business data requirements exceeds more than 50000 records.

Execution Logic

Batch Apex was designed to handle and process large volumes of data. Let say for example, your Salesforce organization as around 5.2 million account records, and the business wants all account record to be updated to have the total associated contacts. Regular apex classes without implementing the batchable interface, would not able to process such large volumes of accounts. Batch Apex provides the capability to divide the 5.2 million records into smaller manageable batches. For example, lets work with a batch size of 200, this means that 5200,000 / 200 = 26000. So 26000 batches will be executed each with 200 records at their disposal. Each time you invoke a batch class, the job is placed on the Apex job queue and is executed as a discrete transaction.

The default batch size is 200 records.

Benefits

Listed below are the main advantages in using the Batch functionality:

  1. With every new batch transaction you are awarded with a fresh dish of governor limits.
  2. If one batch fails to process successfully, all other successful batch transactions aren’t rolled back.

Batch Apex Syntax

To write a Batch Apex class, your class must implement the Database.Batchable interface and include the following three methods:

start In the start method you write the SOQL queries needed to return the records you want to process. This method is called once for every batch transaction.

With the QueryLocator object, the governor limit for the total number of records retrieved by SOQL queries is bypassed and you can query up to 50 million records. However, with an Iterable, the governor limit for the total number of records retrieved by SOQL queries is still enforced.

execute Process the records queried from the start method. Runs for every batch data transaction. Also bare in mind that this method is not guaranteed to execute in the order they are received from the start method.

finish You can use this method to run post processing operations for the executed batch. For example notifying users by email.

Batch class example

The following depicts a basic skeleton for constructing batchable classes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
global class BatchClass implements Database.Batchable<sObject> {
    global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {
        // write your query to generate the batches of records or objects to be passed to execute
    }
    global void execute(Database.BatchableContext bc, List<sObject> records){
        // process each batch of records
    }    
    global void finish(Database.BatchableContext bc){
        // execute any post-processing operations
    }    
}

Invoking a Batch Class

To be able to invoke a Batch class, instantiate the class and call Database.execute(new BatchClass()).

1
2
BatchClass instance = new BatchClass(); 
Id batchId = Database.executeBatch(instance);

You can also optionally pass a second scope parameter to specify the number of records that should be passed into the execute method for each batch.

Note: I would not suggest to exceed a batch size of 200, your batch process might failed due to exceeded governor limits.

Monitor a Batch

The result of the method Database.executeBatch will return the current batch transaction id. You can use that id to query the AsyncApexJob records. Each batch Apex invocation creates an AsyncApexJob record.

1
2
3
// Database.BatchableContext bc , has the method getJobId
AsyncApexJob currentJob = [ SELECT Id, Status, JobsItemsProcessed, TotalJobsItems, 
                            NumberOfErrors FROM AsyncApexJob WHERE Id =:bc.getJobId()];

Best Practices

Please consider the following when using Batch Apex:

  • Tune queries in the start to be as selective as possible to execute faster
  • Only use Batch Apex if you have more than one batch of records
  • Minimize the number of asynchronous requests created to minimize the chance of delays
  • Please don’t invoke a Batch class from a trigger, unless you can guarantee the trigger will not create more batch jobs exceeding the limits. For example recursion for updating or inserting the same object the batch updates or saves.

Things to Remember

Please bare in mind the following considerations:

  1. A maximum of 50 million records can be returned in the QueryLocator object. If more than 50 million records are returned, the batch job is immediately terminated and marked as Failed.
  2. If the start method of the batch class returns a QueryLocator, the optional scope parameter of executeBatch can have a maximum value of 2,000.
  3. Up to 5 batch jobs can be queued or active concurrently.
  4. Methods declared as future can’t be called from a batch Apex class.
  5. All methods in the class must be defined as global or public.

Practical Example

The following code tallies the total number opportunities associated to an Account.

  1. Create a Integer field on Account called “Number of Opportunities”
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
global with sharing class AccountBatch implements Database.Batchable<Sobject> {

    global Database.QueryLocator start(Database.BatchableContext bc){
            return Database.getQueryLocator('SELECT Id,(SELECT Id FROM Opportunities) FROM Account');
    }
    global void execute(Database.BatchableContext bc, List<Account> records){
        List<Account> accounts = new List<Account>();
        for(Account record: records){
            record.Number_of_Opportunities = record.Opportunities.size();
            accounts.add(record);
        }
        if(!accounts.isEmpty()){
            update accounts;
        }
    }
    global void finish(Database.BatchableContext bc){
        System.debug('Batch with ID ' + bc.getJobId() + ' completed.');
    }
}

Testing Example

The following snippet below tests the AccountBatch class.

Note: this example uses the @testSetup annotation. Methods defined with the @testSetup annotation are used for creating common test records that are available for all test methods in the class. You must run the Batch instantiation synchronously using Test.startTest() and Test.stopTest().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@isTest
public class AccountBatchTest {
    @testSetup
    public static void setup(){
        Account accountRecord = new Account(Name='Test One');
        Opportunity opportunityRecord = new Opportunity();
        opportunityRecord.CloseDate = Date.Today().addMonths(2);
        opportunityRecord.Name = 'Awesome Opportunity';

        insert accountRecord;
        opportunityRecord.AccountId = accountRecord.Id;
        insert opportunityRecord;
    }

    @isTest
    public static void testBatchClass(){
        Test.startTest();
        AccountBatch batch = new AccountBatch();
        Id batchId = Database.executeBatch(batch);
        Test.stopTest();
        Account record = [SELECT ID FROM Account WHERE Name = 'Test One'];
        System.assert(record != null);
        System.assert(record.Number_of_Opportunities == 1);
    }
}

Epilogue

In concluding, it can be said that Batch Apex are specifically made to process large volumes of records over sets of batch transactions. We also discussed, the benefits, syntax and use cases for Batch Apex on the Salesforce platform. Code examples were also provided to cement the concepts shown. Thanks for stopping by 😃 and i hope you was able to learn something new or reinforce what you already knew. A través del aprendizaje continuo, ¡mejorará!