SalesforceBlue

Feel the rhythm of Salesforce

Apex

Batch Apex Simplified

Let’s start Batch Apex by knowing it has special privileges 🙂

Yes, it has special privileges from Salesforce when it comes to processing and the governor limits enforcement. So how is it different than other Asynchronous counterparts such as Future methods & Queueable Apex?

Batch Apex allows you to process a large number of data in batches. This large number of data can’t be handled by future or queueable apex.

For Example, If we want to update 50,000 records or more in any transaction either synchronous or asynchronous that can’t be done as there is a governor limit of 10000 records processed as a result of DML in a given transaction.

This can be achieved using Batch Class which will allow us to do processing of very large data sets.

Code block of Batch Apex:
public class BatchApex implements Database.Batchable<sObject> {
    public Database.QueryLocator start(Database.BatchableContext bc){
        String query = 'Select Id, Amount From Opportunity Where Amount > 10000';
        return Database.getQueryLocator(query);
    }
    
    public void execute(Database.BatchableContext bc, List<SObject> records){
        
        // execute Logic
        
    }

    public void finish(Database.BatchableContext bc){
        
        // finish Logic
    }
}

Let’s split the above batch class into sub-segments to know them one by one.

Batch Apex Declaration:

public class BatchApex implements Database.Batchable<sObject>

In order to create a batch class, we have to implement the Database.Batchable<sObject> interface.

start method:

public Database.QueryLocator start(Database.BatchableContext bc){
    String query = 'Select Id, Amount From Opportunity Where Amount > 10000';
    return Database.getQueryLocator(query);
}

start method returns a Database.QueryLocator and is the first method that is executed when a batch class is invoked. It only executes a single time in the batch lifecycle.

It provides the collection of all records that the execute method will process in individual batches.

With the QueryLocator object, the governor limit for the total number of records retrieved by SOQL queries is bypassed and we can query up to 50 million records.

execute method:

public void execute(Database.BatchableContext bc, List<SObject> records){
        
    // execute Logic
    
}

execute method accepts two parameters one is Database.BatchableContext that tracks the progress of the batch job and List<SObject> which holds the records received in execute method.

execute method run multiple times in the whole batch lifecycle depending upon the number of batches formed.

For Example, For a batch job running with a batch size of 200, the start method returned 1000 records. Thus, execute method will run 5 times for 200 records each.

Every call to the execute method is a new transaction with a fresh set of governor limits. This feature makes Batch Apex a powerful way to handle a very large set of records.

If one batch fails then other successful batches are not rolled back.

finish Method:

public void finish(Database.BatchableContext bc){
        
    // finish Logic
}

finish method accepts Database.BatchableContext as a parameter. This method is called once all execute method finishes processing all the batches.

Generally in the finish method, we do some post-processing logic like sending out notifications once the batch job is completed, invoking any other batch jobs, etc.

In the below diagram we have shared the flow of a Batch Class methods :

Invoking A Batch Job:

We can invoke or submit the batch jobs using the Database.executeBatch method which accepts two parameters.

The first parameter is the instance of the batch class which has to be submitted.

The second optional parameter is an Integer which is a number ranged from 1-2000. This is used to set the batch size or the number of records to be passed in execute method call. If you give more than 2000 then by default 2000 will be picked.

If the second parameter is not specified then the default batch size is 200.

ID batchprocessid = Database.executeBatch(new BatchApex(), 200);

Database.executeBatch method returns a Job Id. This Job Id can be used to track the status of the job.

ID batchprocessid = Database.executeBatch(new batchClass());

AsyncApexJob aaj = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID =: batchprocessid ];
Aborting A Batch Job:

The batch jobs can be aborted using System.abortJob method. It accepts a Job Id that has to be aborted.

System.abortJob(batchprocessid);
Callouts In Batch Apex

In order to make a callout to external services, we have to implement the Database.AllowsCallouts interface.

public class BatchApex implements Database.Batchable<sObject>, Database.AllowsCallouts {
    public Database.QueryLocator start(Database.BatchableContext bc){
        String query = 'Select Id, Amount From Opportunity Where Amount > 10000';
        return Database.getQueryLocator(query);
    }
    
    public void execute(Database.BatchableContext bc, List<SObject> records){
        
        // execute Logic
        // Callouts
        
    }

    public void finish(Database.BatchableContext bc){
        
        // finish Logic
    }
}
Maintating State In Batch Apex:

By default, we can’t maintain states in the batch class. This means that the member variables or the attributes of the class are reset every time execute methods run in the batch job.

In order to maintain the state across all the execute method calls, we have to implement the Database.Stateful interface.

public class BatchApex implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {
    public Integer counter = 0;
    
    public Database.QueryLocator start(Database.BatchableContext bc){
        String query = 'Select Id, Amount From Opportunity Where Amount > 10000';
        return Database.getQueryLocator(query);
    }
    
    public void execute(Database.BatchableContext bc, List<SObject> records){
        
        // execute Logic

        counter++;
        if(counter > 1000) {
            // Do a fancy thing !
        }
        
    }

    public void finish(Database.BatchableContext bc){
        
        // finish Logic
    }
}
Batch Apex Governor Limits Considerations:
  • Up to 5 batch jobs can be queued or active concurrently.
  • Up to 100 Holding batch jobs can be held in the Apex Flex Queue.
  • In a running test, you can submit a maximum of 5 batch jobs.
  • The maximum number of batch Apex method executions per 24-hour period is 250,000, or the number of user licenses in your org multiplied by 200—whichever is greater.
  • The start, execute, and finish methods can implement up to 100 callouts each.
Invoking Future & Queable Apex From Batch Class

We can’t call a future method from the Batch context.

We can call a Queueable Apex from the Batch class using the System.enqueueJob method.

public class BatchApex implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {
    public Integer counter = 0;
    
    public Database.QueryLocator start(Database.BatchableContext bc){
        String query = 'Select Id, Amount From Opportunity Where Amount > 10000 Limit 4';
        return Database.getQueryLocator(query);
    }
    
    public void execute(Database.BatchableContext bc, List<SObject> records){
        
        // execute Logic

        // Invoking Queueable Apex
        System.enqueueJob(new QueueableClass());
        
        
    }

    public void finish(Database.BatchableContext bc){
        
        // finish Logic
    }
}
Batch Apex Example:

In this example, we will be categorizing the different Accounts into 3 different categories that are Platinum, Gold & Silver based upon their annual revenue.

Please create a custom field Category on Account before saving the below code in your org.

global class AccountCategorizationBatch implements Database.Batchable<sObject> {

    global Database.QueryLocator start(Database.BatchableContext bc) {
        String query = 'SELECT AnnualRevenue, Category__C FROM ACCOUNT';
        return Database.getQueryLocator(query);                      
        
    }

    global void execute(Database.BatchableContext bc, List<Account> recordList) {
        List<Account> accToUpdate = new List<Account>();

        for (Account acc : recordList) {
            if(acc.AnnualRevenue != null) {
                if(acc.AnnualRevenue >= 2000000) {
                    acc.Category__c = 'Platinum';
                } else if (acc.AnnualRevenue >= 1000000 && acc.AnnualRevenue < 200000) {
                    acc.Category__c = 'Gold';
                } else {
                    acc.Category__c = 'Silver';
                }

                accToUpdate.add(acc);
            }
        }

        if(!accToUpdate.isEmpty()) {
            update accToUpdate;
        }
    }

    global void finish(Database.BatchableContext jobIdParam) {
      
    }
}

With this, we have covered good information about batch classes 🙂

There is one more way to implement batch Apex by using Iterables. You can know more about it over here – Using Iterables In Batch Apex

We have covered a lot of interfaces that are required to be implemented to make a class a Batch class. You can learn more about the interfaces over here – Apex Interfaces.

Thank you for visiting SalesforceBlue.com
If you have any queries feel free to write down a comment below 🙂


Leave a Reply

Your email address will not be published. Required fields are marked *