Introduction to Batch Classes
What is Batch Apex?
Batch Apex is an asynchronous execution of Apex code specially designed in a way so that it can process the large number of records and has greater flexibility in governor limits than the synchronous code.
Scenario: Why do we require Batch Apex, let’s take an example if we want to update
more than 10,000 record using single DML statements in same transaction in this case Salesforce will not allow you to do this due to Salesforce governor limit as you might be aware the using DML Statement you can update up to 10,000 records not more than that so if you want to bypass this Salesforce governor limit the solution is Apex Batch. Apex Batch facilitates asynchronous processing of the records in multiple batches/chunks so that Salesforce governor limit will not hit and you can achieve your purpose too.
Implementing Batch Apex:
Batch Apex implements Database.batchable interface. The interface has three methods named as Start(), Execute() and Finish() methods which should be implemented in the Batch Apex class which is supposed to implements this interface.
Start method: Start method is a point where you can collect your records to be processed by the execute method of the batch apex
Execute method: The actual processing point where your records take place in the execution.
Finish method: Finish method can have post job processing actions like sending an emails or call chain of another batch class if required.
Note: Start and Finish method run only once in a life of batch whereas execute method run multiple times as per size of batch you defined.
Example: To understand this in detail let’s see the below class. In the below program we are going to update all account name in the account object with “*****” after the account name as a prefix.
global class AccountUpdateBatch implements Database.Batchable { global Database.QueryLocator start(Database.BatchableContext BC) { String query = ‘SELECT Id,Name FROM Account’; return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List scope) { for(Account a : scope) { a.Name = a.Name + ‘******’; } update scope; } global void finish(Database.BatchableContext BC) { } }
Flow of Batch Classes
Once a batch class is called with an executeBatch statement, the start method is run first which returns an n number of records.
For example, for the batch class in the example above, here is how we fire the batch class :
Database.executeBatch(new AccountUpdateBatch ());
Here the batch size is 200 records. Now, if the start method returns let’s say 5000 records, then the execute method will run 5000/200, that is 25 times.
NOTE : The default batch size if we don’t specify batch size explicitly the default size is 200. But if you want you can call a batch with the minimum batch size of 1 and maximum batch size of 2000 like this :
Batch Size : 1
Database.executeBatch(new AccountUpdateBatch (),1);
Batch Size : 2000
Database.executeBatch(new AccountUpdateBatch (),2000);
After execute method has run 25 times and there are no more records to process, then the finish method is called last.
NOTE: The execute method in batch class is an overloaded method.
Introduction to Batch Interfaces:
Database.Batchable
This interface needs to be added to each apex class which is to be defined as an apex class. Implementing this interface gives the class access all the batch methods like :
- Start
- Execute
- Finish
Database.Stateful
Once implemented, this interface makes sure that any variables defined in the batch class hold their values across all the execute method processing. For example :
global class batchAccountUpdate implements Database.Batchable,Database.Stateful { global Integer totalRecordsProcessed = 0; global Database.QueryLocator start(Database.BatchableContext BC) { String query = 'SELECT Id,Name FROM Account'; return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List scope) { for(Account a : scope) { a.Name = a.Name + '******'; totalRecordsProcessed ++; } update scope; } global void finish(Database.BatchableContext BC) { system.debug(totalRecordsProcessed); } }
Here, the totalRecordsProcessed variable will be able to store the value of the total number of account records that are processed and can be used in the finish method.
Types of Batches
Database.QueryLocator
This type of batch returns a database.querylocator instance from the start method. Use this when you want the batch to run off a SOQL query inside the start method.
global Database.QueryLocator start(Database.BatchableContext BC) { String query = 'SELECT Id,Name FROM Account'; return Database.getQueryLocator(query); }
Iterable
This type of batches return Iterable<> type data from the start method. Use this when you want to run your batch class on a wrapper class instance and not a SOQL query. For example,here is a wrapper class :
public class AccountWrapper { public Account acc {get;set;} public Boolean selected {get;set;} }
Then you can run your batch class to work on a list of this wrapper class like this :
global Iterablestart(Database.BatchableContext BC) { list acList = new list (); acList.add(new AccountWrapper()); return acList; }
NOTE : While using the iterable batches, the limit on total no. of records that can be returned from the start method is 50000 only.
Error Handling Mechanism in Batch Classes
A really common scenario in batch classes is when we need to collect all failed records and send an email or do post processing in the finish method. This is done to make sure the users are notified of what records failed in the batch class.
For example, let’s take our example batch class which updates the Account names by adding ‘****’ to the end of them. We want to implement an error handling mechanism to this batch to collect all the records which did not update and then send an email to the user for those records. We can implement that by using database.stateful and storing all failed record ids in a list.
global class batchAccountUpdate implements Database.Batchable,Database.Stateful { global list<Account> failedRecords = new list<Account> (); global Database.QueryLocator start(Database.BatchableContext BC) { String query = 'SELECT Id,Name FROM Account' + (failedRecordIs.isEmpty() ? '' : 'WHERE Id IN: failedRecords'); return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List scope) { for(Account a : scope) { a.Name = a.Name + '******'; } try{ update scope; }catch (exception e) { failedRecords.addAll(scope); } } global void finish(Database.BatchableContext BC) { // Send Email to User Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); List<String> sendTo = new List<String>(); sendTo.add(UserInfo.getUserEmail()); mail.setToAddresses(sendTo); mail.setSubject('Batch Failed Records'); String body = 'Failed Records <br/>'; for(Account ac : failedRecords) { body += 'Failed Account Id : ' + ac.Id + '<br/>'; } mail.setHtmlBody(body); Messaging.sendEmail(new list<Messaging.SingleEmailMessage> {mail} ); } }
This class adds the failed records to the failedRecords list by using a try-catch block :
try{ update scope; }catch (exception e) { failedRecords.addAll(scope); }
And since the batch is defined as stateful, this list gets filled with all the failed records and then we can access this list in the finish method and fire the email to the user for these methods.
global void finish(Database.BatchableContext BC) { // Send Email to User Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); List<String> sendTo = new List<String>(); sendTo.add(UserInfo.getUserEmail()); mail.setToAddresses(sendTo); mail.setSubject('Batch Failed Records'); String body = 'Failed Records <br/>'; { body += 'Failed Account Id : ' + ac.Id + '<br/>'; } mail.setHtmlBody(body); Messaging.sendEmail(new list<Messaging.SingleEmailMessage> {mail} ); } }