Working in the shared space of Salesforce causes developers to run into issues. SOQL limits, timeouts, callout sequencing, and more frustrate us. So how do you run large, complex jobs in the cloud platform without worrying about timeouts or exceeding platform limits? Enter batch Apex to execute these large complex jobs.
This post discusses what batch Apex is, how to call it, and from where to call it.
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) { } }
Expand Your Test Coverage
Scope
Scope has two connotations in Apex batch jobs.
The first is related to the records returned from the start method. This scope is the entire set of records to process in the batch job. The scope can vary depending on the return value from the start method.
The second scope defines how many records the execute method receives in each iteration of your job. Depending on the query you perform in your start method, the maximum size you can process at once changes. For example, if you use a QueryLocator, the maximum value is 2,000. Alternatively, if you return an Iterable, there is no upper limit. Still, caution is required here because you could hit 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 is code that you write and execute in the Developer Console. Because it’s run on demand, it can be a convenient place to start your batch class.
It lets you see debug statements you’ve added to your batch class as the job runs.
To run a batch Apex class in Anonymous Apex. First, open the Developer Console in Lightning by going to the gear icon and selecting Developer Console.
Once the Developer Console is open, select Debug and 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.
Then select Execute. The code you entered will run. You can open the logs down below to see what happened.
Schedulable Class
If you want to schedule your batch job to run at regular intervals, such as every day or every week, you can do that using a schedulable class.
Create a new class with the system to schedule your batch job with a Schedulable class.Schedulable interface, or add the interface to your batch class.
Next, add a method called execute that accepts a parameter of SchedulableContext. The execute method is the method that’s called when the system starts the scheduled job. In this method, you’ll instantiate your batch class and call Database.executeBatch or System.scheduleBatch (though calling scheduleBatch from a schedulable doesn’t make much sense). Include as parameters your instance of the batch class and any optional parameters.
All together with the batch and schedulable in one, it looks like this for Database.executeBatch.
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); } }
Once your code saves, you need to schedule when the job runs using a Schedule Apex job.
To do that in Lightning, go to Setup, then in the quick finder, enter Apex. Next, click on Schedule Apex along the top.
In the new screen that opens up, set the required parameters, including your scheduled job class name, and then Save.
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.
Wrapping Up: batch Apex in Salesforce
So there you have it – batch Apex in a nutshell for Salesforce. We’ve covered how to call batch Apex using the two main methods: scheduleBatch and executeBatch. We’ve also looked at how to execute from an Anonymous Apex class and from within a Schedulable. And finally, we took a quick look at some of the best practices for working with batches. So now that you know everything there is to know about batch Apex, what are you going to build? Get out there and do it!