Variables For Salesforce Batch Apex


I’ve come across numerous inquiries on developer forums lately concerning the utilization of variables in batch Apex classes. One query sought guidance on passing a variable to the execute method of a batch class, while another focused on variable accessibility in the start, execute, and finish methods of a batch.

The intention of this post is to demonstrate various aspects of how variables operate within batch Apex classes.

The provided code doesn’t perform any significant tasks, but it is compatible with your Salesforce Org. Feel free to copy and paste it into a new Apex Class.

/*
    Created by: Greg Hacic
    Last Update: 13 August 2019 by Greg Hacic
    Questions?: greg@ities.co
    
    Notes:
        - example of batch apex variables
        - want to run this in your developer org or sandbox?
            - open the developer console in your salesforce Org
            - Execute Anonymous
                batchExample b = new batchExample('No Contact'); //new instance of batchExample
                Id batchProcessId = Database.executeBatch(b, 3); //submits the batchExample batch Apex job for execution with a batch size of 3
*/
public class batchExample implements Database.Batchable<SObject>, Database.Stateful {
    
    private final String finalStringVariable; //final instance variable
    private String stringVariable; //instance variable
    private static String staticStringVariable; //static variable
    private Set<String> setOfContactNames = new Set<String>(); //set of Contact.Name
    private static Set<String> staticSetOfContactNames = new Set<String>(); //set of Contact.Name
    
    //constructor
    public batchExample(String passedString) {
        System.debug('- Constructor: Begin -');
        finalStringVariable = passedString; //assign the passed string to finalStringVariable
        stringVariable = passedString; //assign the passed string to stringVariable
        staticStringVariable = passedString; //assign the passed string to staticStringVariable
        System.debug('finalStringVariable: '+finalStringVariable+' | stringVariable: '+stringVariable+' | staticStringVariable: '+staticStringVariable);
        System.debug('setOfContactNames.size(): '+setOfContactNames.size()+' | staticSetOfContactNames.size(): '+staticSetOfContactNames.size());
        System.debug('- Constructor: End -');
    }
    
    //the "start" method is called at the beginning of a batch Apex job
    //use this method to collect the records (of objects) to be passed to the "execute" method for processing
    public Database.QueryLocator start(Database.BatchableContext bc) {
        System.debug('- start: Begin -');
        System.debug('finalStringVariable: '+finalStringVariable+' | stringVariable: '+stringVariable+' | staticStringVariable: '+staticStringVariable);
        System.debug('setOfContactNames.size(): '+setOfContactNames.size()+' | staticSetOfContactNames.size(): '+staticSetOfContactNames.size());
        System.debug('- start: End -');
        return Database.getQueryLocator('SELECT Id, Name FROM Contact LIMIT 10'); //return the query locator
    }
    
    //the "execute" method is called after the "start" method has been invoked and passed a batch of records
    public void execute(Database.BatchableContext bc, List<SObject> scope) {
        System.debug('- execute: Begin -');
        for (SObject s : scope) { //for all sObjects in the batch
            Contact c = (Contact)s; //cast the Contact object from the scope
            //Final members can only be assigned in their declaration, init blocks, or constructors so the variable cannot be updated here: finalStringVariable = c.Name; //assign the Contact.Name to finalStringVariable
            stringVariable = c.Name; //assign the Contact.Name to stringVariable
            staticStringVariable = c.Name; //assign the Contact.Name to staticStringVariable
            System.debug('finalStringVariable: '+finalStringVariable+' | stringVariable: '+stringVariable+' | staticStringVariable: '+staticStringVariable);
            setOfContactNames.add(c.Name); //add the Contact Name to the set
            staticSetOfContactNames.add(c.Name); //add the Contact Name to the set
            System.debug('setOfContactNames.size(): '+setOfContactNames.size()+' | staticSetOfContactNames.size(): '+staticSetOfContactNames.size());
        }
        System.debug('- execute: End -');
    }
    
    //the "finish" method is called once all the batches are processed
    public void finish(Database.BatchableContext bc) {
        System.debug('- finish: Begin -');
        System.debug('finalStringVariable: '+finalStringVariable+' | stringVariable: '+stringVariable+' | staticStringVariable: '+staticStringVariable);
        System.debug('setOfContactNames.size(): '+setOfContactNames.size()+' | staticSetOfContactNames.size(): '+staticSetOfContactNames.size());
        System.debug('setOfContactNames: '+setOfContactNames);
        System.debug('staticSetOfContactNames: '+staticSetOfContactNames);
        System.debug('- finish: End -');
    }

}

We are iterating through all Contacts in the Org, creating variables with the data, and subsequently displaying the values in the debug log. This serves as a straightforward method to demonstrate how the values evolve and update during batch processing.

After saving your Apex class, you can navigate to the Developer Console and open the Execute Anonymous Window (found within the console by clicking Debug > Open Execute Anonymous Window).

Execute the batch job by copying and pasting the following syntax into the window:

batchExample b = new batchExample('No Contact'); //new instance of batchExample
Id batchProcessId = Database.executeBatch(b, 3); //submits the batchExample batch Apex job for execution with a batch size of 3

Select the two lines and click the button labeled “Execute Highlighted.”

Navigate to the Logs tab situated in the lower section of the Developer Console. A sequence of log entries will appear, depicting the diverse actions undertaken by the batch class. Double-click any log row to open it. Subsequently, click the checkbox adjacent to the text “Debug Only” to reveal the debug statements from our Apex class.

The logic we implemented compelled the batch to handle three records simultaneously. Considering that we restricted the SOQL statement to retrieve only ten records, we should observe one log entry for the start method, one for the constructor, four logs for each processed batch, and a log for the finish method. The exact number of logs may vary, but the key takeaway is that not all information will be consolidated into a single log record. I’ve taken the liberty of extracting the debugs from my sandbox and provided them below:

[24]|DEBUG|- Constructor: Begin -
[28]|DEBUG|finalStringVariable: No Contact | stringVariable: No Contact | staticStringVariable: No Contact
[29]|DEBUG|setOfContactNames.size(): 0 | staticSetOfContactNames.size(): 0
[30]|DEBUG|- Constructor: End -
[36]|DEBUG|- start: Begin -
[37]|DEBUG|finalStringVariable: No Contact | stringVariable: No Contact | staticStringVariable: No Contact
[38]|DEBUG|setOfContactNames.size(): 0 | staticSetOfContactNames.size(): 0
[39]|DEBUG|- start: End -
[45]|DEBUG|- execute: Begin -
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: John Bond | staticStringVariable: John Bond
[54]|DEBUG|setOfContactNames.size(): 1 | staticSetOfContactNames.size(): 1
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Edna Frank | staticStringVariable: Edna Frank
[54]|DEBUG|setOfContactNames.size(): 2 | staticSetOfContactNames.size(): 2
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Avi Green | staticStringVariable: Avi Green
[54]|DEBUG|setOfContactNames.size(): 3 | staticSetOfContactNames.size(): 3
[56]|DEBUG|- execute: End -
[45]|DEBUG|- execute: Begin -
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Stella Pavlova | staticStringVariable: Stella Pavlova
[54]|DEBUG|setOfContactNames.size(): 4 | staticSetOfContactNames.size(): 1
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Lauren Boyle | staticStringVariable: Lauren Boyle
[54]|DEBUG|setOfContactNames.size(): 5 | staticSetOfContactNames.size(): 2
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Tess Dachshund | staticStringVariable: Tess Dachshund
[54]|DEBUG|setOfContactNames.size(): 6 | staticSetOfContactNames.size(): 3
[56]|DEBUG|- execute: End -
[45]|DEBUG|- execute: Begin -
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Jack Rogers | staticStringVariable: Jack Rogers
[54]|DEBUG|setOfContactNames.size(): 7 | staticSetOfContactNames.size(): 1
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Babara Levy | staticStringVariable: Babara Levy
[54]|DEBUG|setOfContactNames.size(): 8 | staticSetOfContactNames.size(): 2
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Jane Grey | staticStringVariable: Jane Grey
[54]|DEBUG|setOfContactNames.size(): 9 | staticSetOfContactNames.size(): 3
[56]|DEBUG|- execute: End -
[45]|DEBUG|- execute: Begin -
[51]|DEBUG|finalStringVariable: No Contact | stringVariable: Jake Llorrac | staticStringVariable: Jake Llorrac
[54]|DEBUG|setOfContactNames.size(): 10 | staticSetOfContactNames.size(): 1
[56]|DEBUG|- execute: End -
[61]|DEBUG|- finish: Begin -
[62]|DEBUG|finalStringVariable: No Contact | stringVariable: Jake Llorrac | staticStringVariable: null
[63]|DEBUG|setOfContactNames.size(): 10 | staticSetOfContactNames.size(): 0
[64]|DEBUG|setOfContactNames: {Avi Green, Babara Levy, Edna Frank, Jack Rogers, Jake Llorrac, Jane Grey, John Bond, Lauren Boyle, Stella Pavlova, Tess Dachshund}
[65]|DEBUG|staticSetOfContactNames: {}
[66]|DEBUG|- finish: End -

Upon reviewing the logs, it becomes evident that static variables reset with each batch, while instance variables remain visible and persist across batches. Items declared as final can only be assigned in their declaration, initialization blocks, or constructors.

In the majority of cases, incorporating the Database.Stateful declaration in your batch classes might be unnecessary. My intention was simply to elucidate for other developers/administrators the behavior of variables within the class when this declaration is used.