Collaborating within Salesforce’s shared environment often leads developers to encounter challenges such as SOQL limits, timeouts, and callout sequencing, causing frustration. How can one effectively execute extensive, intricate tasks on the cloud platform without concerns about timeouts or exceeding limits? Batch Apex comes to the rescue for executing these substantial jobs.
This article delves into understanding batch Apex, its invocation process, and where to initiate it from.
What is Batch Apex in Salesforce?
Batch Apex is a tool that allows Salesforce users to run large jobs by dividing them into smaller batches. These smaller batches help to avoid timeouts and ensure that all data gets processed correctly.
Some examples of when you want to use batch Apex are:
- When you need to process a large number of records
- When you need to perform complex calculations on data
- When you need to make many callouts to an external system
A simple example is a batch Apex class that updates a field on a contact. (Let’s assume we have 1,000,000 contacts, so standard SOQL cannot handle it.) Here’s the code for this class. The batch class consists of three methods: start, execute, and finish.
global class BatchUpdateContact implements Database.Batchable<sObject> { global Database.QueryLocator start(Database.BatchableContext BC) { return Database.getQueryLocator('SELECT Id, FirstName, LastName, Email FROM Contact'); } global void execute(Database.BatchableContext BC, List<Contact> contactList) { for (Contact aContact : contactList) { aContact.AssistantName = ''; } update contactList; } global void finish(Database.BatchableContext BC) { } }
Scope
“Scope” holds dual meanings within Apex batch jobs.
Firstly, it pertains to the records retrieved from the start method, representing the complete set of records for processing in the batch job. This scope’s size can vary based on the start method’s return value.
Secondly, it determines the number of records that the execute method receives during each iteration of the job. This size is influenced by the query executed in the start method, altering the maximum number of records processed at a time. For instance, utilizing a QueryLocator sets the maximum value at 2,000, while returning an Iterable removes this upper limit. However, exercising caution is essential here due to potential constraints on processing or query limits.
How to Call Batch Apex in Salesforce
First, you need to create a class that implements the interface
Database.Batchable.
Next, you invoke the code programmatically with either Database.executeBatch or
System.scheduleBatch.
Using Database.executeBatch
The first way to start a batch class is using Database.executeBatch. This is a straightforward method that runs the job immediately.
The method takes two parameters:
- An instance of a batch class (must have been instantiated)
- Scope, an optional parameter, which is an integer that specifies the number to process in each execution
Database.executeBatch returns the ID of the AsyncApexJob created for this job. You can track the progress of the batch job with this ID.
You would use the following code to execute the batch class above with Database.executeBatch and track the progress.
First, instantiate the class.
BatchUpdateContact batchJob = new BatchUpdateContact();
Next, call Database.executeBatch with the instance and optional parameter and store the AsyncApexJob in the parameter contactJobId. You need only two parameters to execute the job.
Id batchJobId = Database.executeBatch(batchJob, 200);
The following code is optional for tracking the status of the batch job. Then query the AsyncApexJob with the contactJobId.
AsyncApexJob aaj = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID =: batchJobId ]; System.debug('Job Id: ' + batchJobid + ' Status: ' + aaj.Status);
Altogether it looks like this.
BatchUpdateContact batchJob = new BatchUpdateContact(); Id batchJobId = Database.executeBatch(batchJob); AsyncApexJob aaj = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID =: batchJobId ]; System.debug('Job Id: ' + batchJobid + ' Status: ' + aaj.Status);
In addition, if you detect an issue, you can abort the job with the System.abortJob method.
System.abortJob(batchJobId);
Using System.scheduleBatch in Salesforce
The second way to start a batch class uses the System.scheduleBatch method, which allows you to schedule the batch job to run later. Planning a job to run later can be helpful if you want to process data overnight or on a weekend when Salesforce is less busy.
System.scheduleBatch takes four parameters:
- A constructed version of the batch class
- The name of the job
- The time interval, specified in minutes
- Scope is an optional parameter like with executeBatch, which is an integer that specifies the number of records to process in each execution.
System.scheduleBatch returns the ID of the scheduled job (CronTrigger), which you can use to track the status of the job.
First, instantiate the batch class.
BatchUpdateContact batchJob = new BatchUpdateContact();
Then, call System.scheduleBatch with the instance, name, and the time interval and store the scheduled job ID in schedContactJobId. For example, 60 means the job runs 60 minutes from now.
Id schedContactJobId = System.scheduleBatch(batchJob, 'Contact Update Batch', 60);
Next, query the CronTrigger with schedContactJobId to get the current status. The following code is optional.
CronTrigger ct = [SELECT TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :schedContactJobId];
Next, optionally assert that the job hasn’t been triggered yet and print out the time the job will start.
System.assertEquals(0, ct.TimesTriggered); System.debug('Job Id: ' + schedContactJobId + ' NextFireTime: ' + ct.NextFireTime);
All put together; it looks like this.
BatchUpdateContact batchJob = new BatchUpdateContact(); Id schedContactJobId = System.scheduleBatch(batchJob, 'Contact Update Batch', 60); CronTrigger ct = [SELECT TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :schedContactJobId]; System.assertEquals(0, ct.TimesTriggered); System.debug('Job Id: ' + schedContactJobId + ' NextFireTime: ' + ct.NextFireTime);
Executing the Code Programmatically
Executing the code programmatically means running Apex code. There are several options for where you might run your batch class: Anonymous Apex, from a Schedulable class, or a trigger.
Anonymous Apex
Anonymous Apex refers to code authored and executed within the Developer Console. Due to its on-demand execution, it serves as a convenient initial location for your batch class.
It facilitates real-time viewing of debug statements embedded within your batch class during the job execution.To execute a batch Apex class using Anonymous Apex, begin by accessing the Developer Console in Lightning. This can be done by navigating to the gear icon and selecting Developer Console.
After opening the Developer Console, choose “Debug” and then select “Open Anonymous Apex Window.”
A popup window will open. In this window, enter either Database.executeBatch or System.scheduleBatch code as outlined above with the batch class name and any required scope variables.
Next, click on “Execute.” This action will initiate the execution of the entered code. To review the outcome, you can access the logs below to observe the process.
Schedulable Class
To set up your batch job for regular scheduling—like daily or weekly runs—you can utilize a schedulable class.
Begin by creating a new class equipped with the system for scheduling your batch job. You can achieve this by implementing the Schedulable interface within a new class or by adding the interface to your existing batch class.Subsequently, incorporate a method named “execute” that takes a parameter of type SchedulableContext. This “execute” method serves as the entry point called by the system when initiating the scheduled job. Within this method, instantiate your batch class and invoke either Database.executeBatch or System.scheduleBatch (although invoking scheduleBatch from a schedulable might not be necessary). Provide your batch class instance and any optional parameters as arguments.When combining both the batch and schedulable functionalities, for instance, using Database.executeBatch, the setup resembles the following:
global class BatchUpdateContact implements Database.Batchable<sObject>, Schedulable { global Database.QueryLocator start(Database.BatchableContext BC) { return Database.getQueryLocator('SELECT Id, FirstName, LastName, Email FROM Contact'); } global void execute(Database.BatchableContext BC, List<Contact> contactList) { for (Contact aContact : contactList) { aContact.AssistantName = ''; } update contactList; } global void finish(Database.BatchableContext BC) { } //Schedulable methods global void execute(System.SchedulableContext schContext) { BatchUpdateContact batchJob = new BatchUpdateContact(); Id batchJobId = Database.executeBatch(batchJob); } }
After saving your code, the next step involves scheduling the job execution using a Schedule Apex job.
In the Lightning interface, navigate to Setup, then in the quick finder, enter “Apex.” Subsequently, click on “Schedule Apex” located along the top.
Within the newly opened screen, configure the necessary parameters, which include specifying your scheduled job class name, and subsequently, save the settings.
Batch From a Batch
When using the standard UI scheduler to run Apex jobs, the smallest time interval you can specify is one day. If you need to schedule your job to run more frequently, you can schedule the next job in the finish method of the current job.
There are two ways to do this.
First, you can call the scheduleBatch method to run the batch job in a few minutes.
global void finish(Database.BatchableContext BC) { BatchUpdateContact batchJob = new BatchUpdateContact(); Id schedContactJobId = System.scheduleBatch(batchJob, 'Contact Update Batch', 5); }
Second, you can schedule it to run at a specific time with System.schedule. This method takes a job name, cron schedule, and the schedulable class (in this case, your batch class that implements both Batchable and Schedulable). For example, it might look like the following.
global void finish(Database.BatchableContext BC) { BatchUpdateContact batchJob = new BatchUpdateContact(); String schedule = '20 30 * * * ?'; String jobID = System.schedule('Contact Update Job ' + DateTime.now(), schedule, batchJob); }
Note: If you chain batch jobs, ensure you start it from Anonymous Apex. In addition, setting it up to run via the UI will result in the system starting jobs and then those jobs scheduling other jobs.
Triggers
Running a batch job from a trigger involves the same Apex code as above. One issue with running batch jobs from triggers is that you can easily exceed the total number of batch jobs you’re allowed to execute at once if the class you’re executing it on is frequently updated across your organization.