Page Contents
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.
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.
- 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
17 comments
Skip to comment form
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
Author
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
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
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);
};
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
Author
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
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.
Author
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
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
i want to run batch class and get record ids per batch i.e
for batch 1- this record ids has been updated
for batch 2- this record ids has been updated and so on
Please help me with sample code
i have a problem with batcheable class and aggregate query result, is posible to implement a aggregate query result in a batch class?
https://developer.salesforce.com/forums#!/feedtype=SINGLE_QUESTION_DETAIL&dc=Developer_Forums&criteria=ALLQUESTIONS&id=9062I000000DNv4QAG
there is my problem
can you help me out executing a batch apex method using a trigger ?
Record is not updating acc.Name + ‘sfdcpoint’;
It should update with account+sfdcpoint when ever we r updating records why iam unable to see this account name linked with sfdc point pls reply me
How Many Records Can I Update Through Batch Class Using Database
answer to DEV
solution: 50miln records
Hello Ankush,
If we have to find duplicates in Contact records and do a merge of duplicate Contact into Master Contact record, then what would be the code for batch class.
Thanks!
You imply that the class and its methods need to be defined as global. I don’t believe that’s true:
https://developer.salesforce.com/docs/atlas.en-us.238.0.apexcode.meta/apexcode/apex_batch_interface.htm