Requirement: Capture all the failures during a batch process, build an erroneous records CSV file and attach it to the failure email.
Batch Apex: Let’s say you’re running a batch job with 20000 records, salesforce divides this batch into 100 transactions of 200 records each automatically. (Default batch size is 200 records). Let’s say we’ve 1000 erroneous records across those 100 transactions and we don’t know which transactions had errors. How do you maintain the state between these transactions? Database.Stateful allows to maintain state between transactions of a batch job. When using Database.Stateful, only instance member variables retain their values between transactions. Static member variables don’t and are reset between transactions.
For more info: Batch Apex
Scenario: Create a batch process on Accounts, capture the failures in a CSV file and send an email to the batch job creator.
Note: Maximum size for email attachment is 3MB.
Gist Link to the code: BatchApexAccountUpdate.cls
global class BatchApexAccountUpdate implements Database.Batchable<SObject>, Database.Stateful{ global Map<Id, String> errorMap {get; set;} global Map<Id, SObject> IdToSObjectMap {get; set;} global BatchApexAccountUpdate(){ errorMap = new Map<Id, String>(); IdToSObjectMap = new Map<Id, SObject>(); } global Database.QueryLocator start(Database.BatchableContext BC) { return Database.getQueryLocator('Select Id, OwnerId, Name From Account'); } global void execute(Database.BatchableContext BC, List<SObject> scope) { List<Account> accountList = new List<Account>(); for(SObject s : scope){ Account acct = (Account) s; // Logic to update fields on the Account accountList.add(acct); } if(accountList.size() > 0) { List<Database.SaveResult> dsrs = Database.Update(accountList, false); Integer index = 0; for(Database.SaveResult dsr : dsrs){ if(!dsr.isSuccess()){ String errMsg = dsr.getErrors()[0].getMessage(); errorMap.put(accountList[index].Id, errMsg); IdToSObjectMap.put(accountList[index].Id, accountList[index]); } index++; } } } global void finish(Database.BatchableContext BC) { //Send an email to the User after your batch completes if(!errorMap.isEmpty()){ AsyncApexJob a = [SELECT id, ApexClassId, JobItemsProcessed, TotalJobItems, NumberOfErrors, CreatedBy.Email FROM AsyncApexJob WHERE id = :BC.getJobId()]; String body = 'Your batch job ' + 'BatchApexAccountUpdate ' + 'has finished. \n' + 'There were ' + errorMap.size() + ' errors. Please find the error list attached to the Case.'; // Creating the CSV file String finalstr = 'Id, Name, Error \n'; String subject = 'Account - Apex Batch Error List'; String attName = 'Account Errors.csv'; for(Id id : errorMap.keySet()){ string err = errorMap.get(id); Account acct = (Account) IdToSObjectMap.get(id); string recordString = '"'+id+'","'+acct.Name+'","'+err+'"\n'; finalstr = finalstr +recordString; } // Define the email Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage(); // Create the email attachment Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment(); efa.setFileName(attName); efa.setBody(Blob.valueOf(finalstr)); // Sets the paramaters of the email email.setSubject( subject ); email.setToAddresses( new String[] {a.CreatedBy.Email} ); email.setPlainTextBody( body ); email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa}); // Sends the email Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email}); } } }
Comments
Post a Comment