Batch Apex Example In Salesforce

Batch Apex Example In Salesforce

Batch Apex Example In Salesforce

What is Batch Apex in Salesforce?

Batch class in salesforce is used to run large jobs (think thousands or millions of records!) that would exceed normal processing limits. Using Batch Apex, you can process records asynchronously in batches (hence the name, “Batch Apex”) to stay within platform limits. If you have a lot of records to process, for example, data cleansing or archiving, Batch Apex is probably your best solution.

Batch Apex Example In Salesforce

Here’s how Batch Apex works under the hood. Let’s say you want to process 1 million records using Batch Apex. The execution logic of the batch class is called once for each batch of records you are processing. Each time you invoke a batch class, the job is placed on the Apex job queue and is executed as a discrete transaction.

Advantage of using batch Apex

  • Every transaction starts with a new set of governor limits, making it easier to ensure that your code stays within the governor execution limits.
  • If one batch fails to process successfully, all other successful batch transactions aren’t rolled back

 

Batch Apex Syntax

To write a Batch Apex class, your class must implement the Database.Batchable interface and include the following three methods:

start
Start method is automatically called at the beginning of the apex job. This method will collect record or objects on which the operation should be performed. These records are divided into subtasks and pass those to execute method.

Used to collect the records or objects to be passed to the interface method execute for processing. This method is called once at the beginning of a Batch Apex job and returns either a Database.QueryLocator object or an Iterable that contains the records or objects passed to the job.

Most of the time a QueryLocator does the trick with a simple SOQL query to generate the scope of objects in the batch job. But if you need to do something crazy like loop through the results of an API call or pre-process records before being passed to the execute method, you might want to check out the Custom Iterators link in the Resources section.

With the QueryLocator object, the governor limit for the total number of records retrieved by SOQL queries is bypassed and you can query up to 50 million records. However, with an Iterable, the governor limit for the total number of records retrieved by SOQL queries is still enforced.

execute
Execute Method performs an operation which we want to perform on the records fetched from start method.

Performs the actual processing for each chunk or “batch” of data passed to the method. The default batch size is 200 records. Batches of records are not guaranteed to execute in the order they are received from the start method.

This method takes the following:

  • A reference to the Database.BatchableContext object.
  • A list of sObjects, such as List<sObject>, or a list of parameterized types. If you are using a Database.QueryLocator, use the returned list.
finish

Finish method executes after all batches are processed. Use this method to send confirmation email notifications

Used to execute post-processing operations (for example, sending an email) and is called once after all batches are processed.

Here’s what the skeleton of a Batch Apex class looks like:

global class MyBatchClass implements Database.Batchable<sObject> {
    global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {
        // collect the batches of records or objects to be passed to execute
    }
    global void execute(Database.BatchableContext bc, List<P> records){
        // process each batch of records
    }    
    global void finish(Database.BatchableContext bc){
        // execute any post-processing operations
    }    
}

Key points about batch Apex

  • To use batch Apex, write an Apex class that implements Database.Batchable interface and make your class global
  • Implement the following 3 methods
      • start()
      • execute()
      • finish()
  • The default batch size is 200

 

Monitoring Batch Apex

To monitor or stop the execution of the batch Apex job, from Setup, enter Apex Jobs in the Quick Find box, then select Apex Jobs.

Batch class in salesforce

Here is example of Batch class in salesforce

global class BatchApexExample implements Database.Batchable<sObject> {
    global Database.QueryLocator start(Database.BatchableContext BC) {
        // collect the batches of records or objects to be passed to execute
        
        String query = 'SELECT Id, Name FROM Account';
        return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<Account> accList) {
       
        // process each batch of records default size is 200
        for(Account acc : accList) {        
           	// Update the Account Name 
            acc.Name = acc.Name + 'sfdcpoint';
        }
        try {
        	// Update the Account Record
            update accList;
        
        } catch(Exception e) {
            System.debug(e);
        }
        
    }   
    
    global void finish(Database.BatchableContext BC) {
    	// execute any post-processing operations like sending email
    }
}

Invoking a Batch Class

To invoke a batch class, simply instantiate it and then call Database.executeBatch with the instance:

MyBatchClass myBatchObject = new MyBatchClass(); 
Id batchId = Database.executeBatch(myBatchObject);

You can also optionally pass a second scope parameter to specify the number of records that should be passed into the execute method for each batch. Pro tip: you might want to limit this batch size if you are running into governor limits.

Id batchId = Database.executeBatch(myBatchObject, 100);

Each batch Apex invocation creates an AsyncApexJob record so that you can track the job’s progress. You can view the progress via SOQL or manage your job in the Apex Job Queue. We’ll talk about the Job Queue shortly.

AsyncApexJob job = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID = :batchId ];

 

Scheduling Batch Apex

can also use the Schedulable interface with batch Apex classes. The following example implements the Schedulable interface for a batch Apex class called batchable:

global class scheduledBatchable implements Schedulable {
   global void execute(SchedulableContext sc) {
      BatchApexExample b = new BatchApexExample(); 
      Database.executeBatch(b);
   }
}

 

Test class for batch Apex

@isTest
private class BatchApexExampleTest {
    static testmethod void test() {
        // Create test accounts to be updated by batch
	Account[] accList = new List();
	for (Integer i=0;i<400;i++) {
	    Account ac = new Account(Name = 'Account ' + i);
	    accList.add(ac);
	}
	insert accList;

        Test.startTest();
            BatchApexExample b = new BatchApexExample();
	    Database.executeBatch(b);
        Test.stopTest();
        // Verify accounts updated
	Account[] accUpdatedList = [SELECT Id, Name FROM Account];
	System.assert(accUpdatedList[0].Name.Contains('sfdcpoint'));
    }
}

Best Practices for batch Apex

As with future methods, there are a few things you want to keep in mind when using Batch Apex. To ensure fast execution of batch jobs, minimize Web service callout times and tune queries used in your batch Apex code. The longer the batch job executes, the more likely other queued jobs are delayed when many jobs are in the queue. Best practices include:

  • Only use Batch Apex if you have more than one batch of records. If you don’t have enough records to run more than one batch, you are probably better off using Queueable Apex.
  • Tune any SOQL query to gather the records to execute as quickly as possible.
  • Minimize the number of asynchronous requests created to minimize the chance of delays.
  • Use extreme care if you are planning to invoke a batch job from a trigger. You must be able to guarantee that the trigger won’t add more batch jobs than the limit.

 

For more details about batch apex refer to batch apex trailhead

Permanent link to this article: https://www.sfdcpoint.com/salesforce/batch-apex-example-in-salesforce/

9 comments

Skip to comment form

    • Alexis Pérez on March 20, 2020 at 9:02 pm

    Is it possible to calculate averages within a batch and that in each block that is processed can this information be preserved to obtain a total calculation of all the records?

    I need to calculate averages per month, by municipality, state and country of many records, but this question arises

    I look forward to your comments, greetings

    1. Yes, Its possible to preserve information across different batches created from single batch run. You need to implement Database.Stateful class for that in your batch class
      Here is more detail about it
      https://developer.salesforce.com/forums/?id=906F00000008zJ5IAI

      • Nikita on April 4, 2020 at 1:19 am

      Hello,

      I need to get all of the unsuccessful records while doing upsert and segregate on those unsuccessful records on the basis of status field

    • divya on May 12, 2020 at 1:15 pm

    HI ,
    Can you please help me to convert following apex into batch apex.
    it is basically creating 3 account and for every account almost 1000+ records are linked and with that orders order items.

    it like 1 account -> 1000 order-> 1000 orders with -> each with one order item.
    I shall be very gratefull to you.

    Note – This code is working perfectly but sometimes gives a CPU ran out of time errror

    var apexBody = [

    ‘Integer no_of_account=3;’,

    ‘Map accountOrderNoMap = new Map();’,
    ‘accountOrderNoMap.put(0, 50);’,
    ‘accountOrderNoMap.put(1, 300);’,
    ‘accountOrderNoMap.put(2, 1100);’,

    ‘Entity__c entity = [SELECT Id FROM Entity__c WHERE Name =’ +
    ‘\’International Society for the Exceptionally Nimble\’ LIMIT 1];’,
    ‘PriceClass__c price = [SELECT Id FROM PriceClass__c WHERE Name =’ +
    ‘\’Default\’ LIMIT 1];’,
    ‘Product__c product = [SELECT Id FROM Product__c WHERE Name =’ +
    ‘\’Accounting Handbook – Digital Download\’ LIMIT 1];’,

    ‘List a = New List();’,
    ‘for(Integer iCount_account = 0; iCount_account < no_of_account; iCount_account++){',
    'a.add(New Account(FirstName=\'Account \'+iCount_account,',
    'LastName=\'PayTest\',BillingStreet= \'45 Newbury Street\',',
    'Status__c= \'Active\',BillingCity= \'Boston\',BillingState =\'MA\',',
    'BillingPostalCode= \'02116\',',
    'BillingCountry= \'United States\',',
    'Salutation= \'Mr.\') );',
    '}',
    'insert a;',

    'List orderlist= new List();’,
    ‘for(Integer iCount_acc= 0; iCount_acc < no_of_account; iCount_acc++){',
    'Integer no_of_order = accountOrderNoMap.get(iCount_acc);',
    'for(Integer iCountOrder= 0; iCountOrder< no_of_order; iCountOrder++){',
    'Order__c create_order = new Order__c();',
    'create_order.BillTo__c = a[iCount_acc].id;',
    'create_order.Purpose__c= \'Merchandise\';',
    'create_order.TransactionDate__c= Date.today();',
    'create_order .Entity__c= entity.Id;',
    'orderlist.Add(create_order);',
    '}',
    '}',
    'insert orderlist;',

    'Integer prev_index = 0;',
    'List orderItemlist= new List();’,
    ‘for(Integer iCount_acc_order = 0; iCount_acc_order < no_of_account; iCount_acc_order++){',
    'Integer no_of_order = accountOrderNoMap.get(iCount_acc_order);',
    'for(Integer iCount_orderItem = prev_index; iCount_orderItem < no_of_order + prev_index; iCount_orderItem++){',
    'OrderItem__c createItem = new OrderItem__c();',
    'createItem.PriceClass__c = price.Id;',
    'createItem.Order__c= orderlist[iCount_orderItem].id;',
    'createItem.Customer__c= a[iCount_acc_order].id;',
    'createItem.Entity__c= entity.Id;',
    'orderItemlist.Add(createItem);',
    '}',
    'prev_index = prev_index + no_of_order;',
    '}',
    'insert orderItemlist;',

    'List orderItemLineList= new List();’,
    ‘for(Integer iCount_orderitemLine = 0; iCount_orderitemLine < prev_index; iCount_orderitemLine++){',
    'OrderItemLine__c ItemLine = new OrderItemLine__c();',
    'ItemLine.Quantity__c = 1;',
    'ItemLine.OrderItem__c= orderItemlist[iCount_orderitemLine].id;',
    'ItemLine.Product2__c= product.Id;',
    'ItemLine.UnitPrice__c= 29;',
    'orderItemLineList.Add(ItemLine);',
    '}',
    'insert orderItemLineList;',

    ].join('\n');
    var executeApex = container.get('apex-service');
    return executeApex(apexBody);
    };

    • sai on May 21, 2020 at 10:41 am

    If I have given batch size as 20 and if one of the record in those 20 records having error will that entire chunk will fail to insert

    1. If you will use above code then all 20 records will fail. But you have option to use Database.update(accList,false)
      It will allow the partial updates. Please refer to
      https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_database.htm

    • VJ on July 15, 2020 at 3:33 pm

    Hi Ankush,

    You mentioned “you might want to check out the Custom Iterators link in the Resources section”. Could you please provide a link for this one.

    1. Here is link
      https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm

      Please search for this ‘Using an Iterable in Batch Apex to Define Scope’ in above link

    • Garapati on August 11, 2020 at 4:49 pm

    hi,
    how to a batch apex to insert records , all examples that I found has only either update or delete
    please help me with the code to insert records using batch apex

Leave a Reply

Your email address will not be published.