Tuesday, June 27, 2017

Batch Class in Salesforce?

Batch Apex :

By using Batch apex classes we can process the records in batches in asynchronously.

If you have a large number of records to process, for example, for data cleansing or archiving, batch Apex is your solution.
The execution logic of the batch class is called once for each batch of records.
The default batch size is 200 records.
You can also specify a custom batch size. Furthermore, each batch execution is considered a discrete transaction.

With each new batch of records, a new set of governor limits is in effect. In this way, it’s easier to ensure that your code stays within the governor execution limits.


Another benefit of discrete batch transactions is to allow for partial processing of a batch of records in case one batch fails to process successfully, all other batch transactions aren’t affected and aren’t rolled back if they were processed successfully.


We must Write the apex class that must be implements the Database.Batchable Interface.


Batch apex class Syntax:

?
1
2
global Class cleanup implements Database.Batchable< sobject >{
}

Batch apex class contain 3 methods


Start Method:


1.Use the start method to collect the records or objects to be passed to the interface method execute.

2.Start method execute only one time.

Syntax for Start Method:

?
1
2
global (Database.Querylocator|Iterable(< sobject>)start(Database.BatchableContext bc){
}
Database.QueryLocator

1.Database.QueryLocator object when you are using a simple query (SELECT).

2.If you use a querylocator object, the governor limit for the total number of records retrieved by SOQL queries is bypassed. (Default 50,000 It allow up to 50 million records).
Iterable

1.Whenever we write the Complex queries that time the Written type of the method should be Iterable.
2.If you use an iterable, the governor limit for the total number of records retrieved by SOQL queries is still enforced.

Execute Method:

Execute method is called for each batch of records.
Batches of records are not guaranteed to execute in the order they are received from the start method.

Syntax for Execute 

?
1
global void execute(Database.BatchableContext BC,List
){ }

Finish Method:


The finish method is called after all batches are processed. Use this method to send confirmation emails or execute post-processing operations.


Each execution of a batch Apex job is considered a discrete transaction.


For example, a batch Apex job that contains 1,000 records and is executed without the optional scope parameter from Database.executeBatch is considered five transactions of 200 records each.


The Apex governor limits are reset for each transaction.

If the first transaction succeeds but the second fails, the database updates made in the first transaction are not rolled back.

Syntax for Finish Method

?
1
2
global void Finish(Database.BatchableContext BC){
}
Returns the ID of the AsyncApexJob object associated with this batch job as a string. Use this method to track the progress of records in the batch job. You can also use this ID with theSystem.abortJob method.

How to execute the batch apex class using developer console?

Batch Apex Class:

global class accountList implements Database.Batchable<Sobject> {
global Database.querylocator start(Database.BatchableContext BC) {
return Database.getQueryLocator([select Id, Name from Account]);
}
global void execute(Database.BatchableContext BC, list<Account> scope) {
  list<account> lstacc = new list<account>();
  for(account acc : scope){
  acc.name = 'Mr'+acc.name;
  lstacc.add(acc); 
 }
Update lstacc;}
global void finish(Database.BatchableContext BC) {
}
}

Execute the Batch Apex Class using Developer Console using below,

accountList objTest = new accountList();
Database.executebatch(objTest);


--------------------------
public class BatchClassforcountaccTYPE implements Database.batchable<Sobject>,Database.Stateful{
public integer count = 0;
    public integer count1 = 0;
    public integer count2 = 0;
public list<account> lstacc1;
public Database.QueryLocator start(Database.BatchableContext bc){
String str ='select id,type from account';
return Database.getQueryLocator (str);
}

public void execute(Database.BatchableContext bc, List<account>lstacc){
    lstacc1 = new list<account>();
for(account acc :lstacc){
    if(acc.type == 'prospect'){ 
   // lstacc1.add(acc);
   // system.debug('------->'+acc.id);
    count++;
    system.debug('=======>'+count);
}
    else if(acc.type == 'Customer - Direct'){
        count1++;
        system.debug('&&&&&&&&&>'+count1);
    }
    else if(acc.type == 'Customer - Channel'){
        count2++;
        system.debug('&&&&&&&&&>'+count2);
    }
}
 //update lstacc1;
/*try{
      
database.saveresult[] ds =  Database.update(lstacc1,false);

for(integer i =0; i<lstacc1.size();i++){
        String msg='';
        If(!ds[i].isSuccess()){
                msg += userList.get(i).id + '\n'+'Error: "';        
                for(Database.Error err: ds[i].getErrors()){  
                     msg += err.getmessage()+'"\n\n';
                } 
        }
        if(msg!='')
            exception_List.add(msg);
     } 

    for(database.SaveResult d : ds){
        if(d.issuccess()){
            count++;
        }
        
    }
}
catch(exception e){
system.debug('update failed');
}*/
}

public void finish(Database.BatchableContext bc){
    system.debug('00000000000>'+count);
    system.debug(count);
//Send an email to the User after your batch completes
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {'venkat81215@gmail.com'};
mail.setToAddresses(toAddresses);
mail.setSubject('Batch Apex Job is done');
mail.setPlainTextBody('Account type is prospect' +'  '+ count +'  '+ 'account type is Customer - Direct'+''+count1+''+'account type is Customer - channel'+''+count2);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}

}
--------------------------

How to log status of a completed Batch Job Status

While we are processing bulk records in a batch Apex, we may need to know the status or want to keep log of completed batch.
For example – one batch job is scheduled at night 4.00 AM, and every day we need to track whether current job status, whether It was success or failure.
One way is to check the log at SetUp | Administration Setup | Monitoring | Apex jobs.
But we can get same thing in Apex , within Finish method of batch class. and then we can log the same in a Custom Object or we can send it via Email to Admin user or any other user.
please find the below sample code , which send status via Email

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
global void finish(Database.BatchableContext BC){
   // Get the ID of the AsyncApexJob representing this batch job
   // from Database.BatchableContext.
   // Query the AsyncApexJob object to retrieve the current job's information.
   AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,
      TotalJobItems, CreatedBy.Email,ExtendedStatus,JobType,ApexClassId,MethodName,
      FROM AsyncApexJob WHERE Id =
      :BC.getJobId()];
   // Send an email to the Apex job's submitter notifying of job completion.
   Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
   String[] toAddresses = new String[] {a.CreatedBy.Email};
   mail.setToAddresses(toAddresses);
   mail.setSubject('Batch Class Status ' + a.Status);
   mail.setPlainTextBody
   ('The batch Apex job processed ' + a.TotalJobItems +
   ' batches with '+ a.NumberOfErrors + ' failures.'+
   '\n' + ' Apex Class:' + a.ApexClassId);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); }
-----------------------------

How to Execute the Batch Apex Class for Every 30 minutes?

We can achieve using System.schedule method.

Below is the Example – we need to execute this from the system log or anonymous apex section of the IDE:



batchApexMethod exeClass = new batchApexMethod();
String cronStr = '0 0,30 * * * *';
System.schedule('Process Accs Job', cronStr, exeClass);

Asynchronous Apex- Apex Scheduler, Batch Apex & Future Methods Implementation Tricks



Salesforce provide different options to run asynchronous jobs which can run in background whenever salesforce resources will be available.

Apex Scheduler is one of the options through which you can schedule a job to run after certain interval of time or to run at particular time everyday, every week, every month etc.

Batch Apex allows you to process large volume of data. Normal apex class will hit governor limits if we process large volume of data. Batch Apex split the records in different batched and you will get new governor limits for each batch.

Future methods runs asynchronously which can be used to perform some operation in background. When you are executing particular code and you want to perform some other operation and don't want to wait for it to get completed, then you can use future method to perform that operation and you can continue executing your code. You can consider that future methods starts new thread or execution.

Now we are going to cover different scenarios related to future methods and apex schedulers and batch apex.

  • Is it possible to call future method from apex scheduler or not?
         Yes
Below is sample code which I tested for this scenario. After scheduling the scheduler, I checked the debug logs and it was displaying logs for future handler  and debug statement present in future method was present in logs.

//Scheduled Apex
public class DemoScheduler1 implements Schedulable{
    public void execute(SchedulableContext sc){
        system.debug('*******Going to call future method ');
        DemoAsynchronousTest.futureMethodCallFromScheduler();
    }
}
//apex class containing future method
public class DemoAsynchronousTest{
    @future
    public static void futureMethodCallFromScheduler(){
        system.debug('******futureMethodCallFromScheduler get called');
    }
 
}

  • Is it possible to do Synchronous Web service callouts from scheduled apex?
          No.
Synchronous Web service callouts are not supported from scheduled Apex. To be able to make callouts, make an asynchronous callout by placing the callout in a method annotated with @future(callout=true) and call this method from scheduled Apex. However, if your scheduled Apex executes a batch job, callouts are supported from the batch class.

  • Can we call scheduler from future method?
Yes

  • Can we call future method from batch apex (from execute method)?
No

  • Is there is any way through which we can call future method from batch apex?
As we know that a webservice can be called from batch class and webservice can call @future method. So in your batch class call webservice and which can call your @future method.         
Also you can call future method from finish method in batch class.

  • What all different things which we need to consider while using future methods?
  1. Methods with the future annotation cannot be used in Visualforce controllers in either getMethodName or setMethodName methods, nor in the constructor.
  2. You cannot call a method annotated with future from a method that also has the future annotation. Nor can you call a trigger from an annotated method that calls another annotated method.
  3. Future methods cannot be called from batch class.

  • Can we modify the scheduler class or classes referenced by this scheduler class if any scheduled job is pending?
          No
If there are one or more active scheduled jobs for an Apex class, you cannot update the class or any classes referenced by this class through the Salesforce user interface. However, you can enable deployments to update the class with active scheduled jobs by using the Metadata API.

  • What we can do if we have to deploy the scheduler class or classes referenced by this scheduler class if any scheduled job is pending for that scheduler class in target org?
By default, changes to Apex code that have Apex jobs pending or in progress can’t be deployed. To deploy these changes, do one of the following.
  • Cancel Apex jobs before deploying changes to Apex code. Reschedule the jobs after the              deployment.
  • Enable deployments with Apex jobs in the Salesforce user interface in the Deployment Settings page.



How to handle error records in Batch apex?

Ex:Need to implement :
public class SimpleBatch implements Database.Batchable<sObject>{

    public Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator('SELECT Id,Name,Start_Date__c,End_Date__c,Active__c FROM 
                        Object__c WHERE Start_Date__c!=NULL AND End_Date__c!=NULL');
    }

    public void execute(Database.BatchableContext BC,List<Package__c> scope){
        Date todayDate = Date.today();
        List<Object__c> objsToUpdate = new List<Object__c>();
        for(Object__c obj:scope){
            if((obj.Start_Date__c <= todayDate) && (obj.End_Date__c >=todayDate)){
                if(pkgObj.Active__c != 'Yes'){
                    pkgObj.Active__c = 'Yes';
                    objsToUpdate.add(obj);
                }
            }else if((obj.Start_Date__c > todayDate) || (obj.End_Date__c < todayDate)){
                if(obj.Active__c != 'No'){
                    obj.Active__c = 'No';
                    objsToUpdate.add(obj);
                }
            }
        }
        update objsToUpdate;
    }

    public void finish(Database.BatchableContext BC){
        //code to send email that batch finished successfully.
    }

}

Solution:


  1. first of all you need to make your batch class stateful using Database.Stateful so replace your first line with
    public class SimpleBatch implements Database.Batchable<sObject>,Database.Stateful{
  2. A global variable required which will maintained failed record.
    global List<String> exception_List;
  3. Use Database.update method instead of update with allOrNothing = false parameter which will return how many records has passed and failed in update call.
    Database.SaveResult[] SaveResultList = Database.update(objsToUpdate,false);   
  4. You will iterate saveResultList and you will add failed records in exception_list
     for(integer i =0; i<objsToUpdate.size();i++){
            String msg='';
            If(!SaveResultList[i].isSuccess()){
                    msg += userList.get(i).id + '\n'+'Error: "';        
                    for(Database.Error err: SaveResultList[i].getErrors()){  
                         msg += err.getmessage()+'"\n\n';
                    } 
            }
            if(msg!='')
                exception_List.add(msg);
         } 
  5. You can use this exception_list in execute method to send in your final email.

Interview Questions on Batch Apex In salesforce

1.What are the soql limitations in apex?
Ans: Total number of records retrieved by SOQL queries-50,000

2.What are the transaction limitations in apex?
Ans: Each execution of a batch Apex job is considered a discrete transaction.
For example, a batch Apex job that contains 1,000 records and is executed without the optional scope parameter from Database.executeBatch is considered five transactions of 200 records each.
The Apex governor limits are reset for each transaction.
If the first transaction succeeds but the second fails, the database updates made in the first transaction are not rolled back.

3.What is the need of batch apex?
Ans: By using Batch apex classes we can process the records in batches in asynchronously.

4.What is Database.Batchable interface?
Ans: The class that implements this interface can be executed as a batch Apex job.

5.Define the methods in batchable interface?
Ans:
Start:
global Database.Querylocator start (Database.BatchableContext bc){}
Execute:
global void execute(Database.BatchableContext bc,List <p>){}
Finish:
global void finish(Database.BatchableContext bc) {}

6.What is purpose of start method in batch apex?
Ans: It collect the records or objects to be passed to the interface method execute.

7.What is the Database.QueryLocator?
Ans: If we use a Database.QueryLocator,
the governor limit for the total number of records retrieved by SOQL queries is bypassed. (Default 50,000 It allow up to 50 million records).

8.What is the iterable<Sobject>?
Ans: If you use an iterable,
the governor limit for the total number of records retrieved by SOQL queries is still enforced.

9.What is the use of execute method?
Ans: Contains or calls the main execution logic for the batch job.

10.How many times execute method is called?
Ans: Execute method is called for  each batch of records.

11.What is the scope of execute method?
Ans: The maximum value for the optional scope parameter is 2,000

12.Can we call callouts from batch apex?
Ans: Yes we can call.

13.Can we call another batch apex from batch apex?
Ans: Yes you can call a batch apex from another batch apex .Either in start method or in finish method you can call other batch

14.How many callouts we can call in batch apex?
Ans: Batch executions are limited to one callout per execution.

15.Batch is synchronous or Asynchronous operations?
Ans: Asynchronous operations.

16.What is the maximum size of the batch and minimum size of the batch ?
Ans: The default batch size is 200 records. min?
max?

17.What is the Database.BatchableContext?
Ans: BatchableContext Interface is Represents the parameter type of a batch job method and
contains the batch job ID. This interface is implemented internally by Apex.

18.How to track the details of the current running Batch using BatchableContext?
Ans: You can check the AsyncApexJob.Status using the JobId from the Database.BatchableContext.

19.How many batch jobs can be added to queue?
Ans: Queued counts toward the limit of 5.

20.What is Database.State full interface?
Ans:To maintain variable value inside the Batch class, Database.Stateful is used.

21.What is Database.AllowCallouts?
Ans:
To use a callout in batch Apex, you must specify Database.AllowsCallouts in the class definition. For example:
global class SearchAndReplace implements Database.Batchable<sObject>,
   Database.AllowsCallouts{
              //Business logic you want by implementing Batchable interface methods
}
Callouts include HTTP requests as well as methods defined with the webService keyword.

22.What is AsyncApexJob object?
Ans: AsyncApexJob is Represents an individual Apex sharing recalculation job.
Use this object to query Apex batch jobs in your organization.

23.When a BatchApexworker record is created?
Ans: For each 10,000 AsyncApexJob records, Apex creates one additional AsyncApexJob record of type BatchApexWorker for internal use. 

Examples of Batch classes
global with sharing class BatchClassforUser implements Database.Batchable<sobject>,Database.Stateful,Schedulable
{
     global final String query;
         global BatchClassforUser(){
                 }
       global BatchClassforUser (String q)
       {
       query = q;
              }
   global Database.QueryLocator start(Database.BatchableContext BC){
                 return Database.getQueryLocator(query);
        } 
      global static string scheduleMe(){
        BatchClassforUser SC = new BatchClassforUser();
        string sched = '0 0 0 1 1/3 ? *'; 
        return System.schedule('Customer_Existing', sched,new BatchClassforUser()); 
    }
          Global void execute(SchedulableContext SC) 
    {
     string qry = 'select id, name,email, contact.accountid, accountid, lastlogindate from user where  isactive = true and lastlogindate < LAST_N_MONTHS:6 and usertype =\'powercustomersuccess\'';
     BatchClassforUser b= new BatchClassforUser (qry);
     database.executebatch(b);
       }
    global void execute(Database.BatchableContext BC, List<sObject> scope )
    {
         List<user> listusr =scope;
        set<id> Objaccid = new set<id>();
        set<id> ActiveUserContactIdsforAccounts = new set<id>();
        for(user usr_rec: listusr )
        {
        Objaccid.add(usr_rec.contact.accountid);
       system.debug('venkat @@@@@@@@@'+Objaccid);
        }
        for(user usr_rec:[select id , contactid from user  where contact.accountid in: Objaccid and isactive = true and  usertype ='powercustomersuccess' and contactid  != null  ])
                                 { 
                                 ActiveUserContactIdsforAccounts.add(usr_rec.contactId);
                                 }
                                 
         OrgWideEmailAddress__c orgemailid =OrgWideEmailAddress__c.getInstance('SonusServiceSystemUpdate');                        
         List<Messaging.SingleEmailMessage> lstmail = new List<Messaging.SingleEmailMessage>();
       //  List<EmailTemplate> lstemp = [Select ID,Name From EmailTemplate  where Name ='InActiveUser' limit 1  ];
       //  system.debug('email template error'+lstemp );
           for(AccountContactRole  Objaccrole : [Select Role, IsPrimary, Id, ContactId, AccountId,contact.Name,
                                             Contact.Email,Contact.ownerid 
                                             From AccountContactRole
                                             where Role = 'Customer Primary Contact' and contact.IsEmailBounced = false
                                             and accountid IN : Objaccid and contact.email !=NULL ] )
        {
         system.debug('venkat ###########'+Objaccrole.contact.email);
         if(ActiveUserContactIdsforAccounts.contains(Objaccrole.contactId))
             {
                string str = '';
                str='<html>';
                str=str+'<body>';
                str=str+'Hello '+Objaccrole.contact.Name+',<br/>'; 
                str=str+'<p>  Sonus has added additional security improvements to the user portal to ensure the safety of your company’s confidential information.<br/> </p>';
                str=str+'<p>  You have been identified as the ‘Primary Contact’ for your company.  This role requires you to manage all user accounts from your company.  As ‘Primary Contact’, you can activate and delete user accounts, and define what areas users can access when logged into the Sonus Networks user portal.  <br/> </p>';
                str=str+'<p>  Please take a few moments to ensure that only users from your company will have access to your confidential information.';
                str=str+'<ul>';
                str=str+'<li> Log into the user portal and verify each user </li>';                
                str=str+'<li> Verify each user has access to the appropriate areas of information</li> ';
                str=str+'<li> Inactivate any employees who have left your company </li> ';
                str=str+'</ul> </p> ';
                str=str+'We will send you this reminder on a quarterly basis as a security reminder to check your user accounts.<br/>';
                str=str+'<p> Thank you for being part of the Sonus team!  </p><br/>';
                str=str+'<p><b> Sonus Global Services Team </b></p>';
                str=str+'</body>';
                str=str+'</html>';
                 String[] toAddresses = new String[] {}; 
                 toAddresses.add(Objaccrole.Contact.Email);
                 system.debug('venkat ZZZZZZZZZZ'+toAddresses);
                 Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                 mail.setToAddresses(toAddresses);
                 system.debug('venkat ------->'+toAddresses.size());
                 //mail.setTargetObjectId(Objaccrole.contactId);
                 //mail.setWhatId(Objaccrole.AccountId);
                 //mail.setUseSignature(true);
                  mail.setSubject('Notification for Login Details');
                  mail.setHtmlBody(str);
                 //mail.setTemplateId(lstemp[0].id );
                 mail.setOrgWideEmailAddressId(orgemailid.Sonus_Service_System__c);
                 //mail.setSaveAsActivity(false); 
                 lstmail.add(mail);
       
             }
         }                                          
       Messaging.SendEmailResult[] results = Messaging.sendEmail(lstmail);
       System.debug('*************results: '+results);
        }
 global void finish(Database.BatchableContext BC)
  {
  }

}

Trigger to Batch class:


Trigger:

trigger BatchClassTrigger on ZensarA__c (After update) {
    for(ZensarA__c za : trigger.new){
        if(trigger.newmap.get(za.id).rating__c != trigger.oldmap.get(za.Id).rating__c){
           Database.executeBatch(new BatchclassforTrigger(trigger.newmap));
        }
        else{
            za.Rating__c.adderror('DUplicate Type');
        }
    }

}
Class:
global class BatchclassforTrigger implements database.Batchable<sobject>,Database.Stateful{
    string query;
    string emails;
    list<zensarb__c> lstzb = new list<zensarb__c>();
    map<id,zensara__c> mapza = new map<id,zensara__c>();
    global BatchclassforTrigger(map<id,zensara__c> mapza1){
        mapza = mapza1;
    }
    global Database.QueryLocator start(Database.BatchableContext BC){
                 return DataBase.getQueryLocator([SELECT Id,name,email1__c,texta__c,zensara__c FROM zensarb__c WHERE zensara__c IN : mapza.keySet()]);
        } 
    global void execute(Database.BatchableContext BC, List<zensarb__c> scope ){
        for(zensarb__c zb : scope){
            zb.Email1__c = mapza.get(zb.ZensarA__c).email1__c;
            emails = zb.Email1__c;
            lstzb.add(zb);
        }
       list<Database.upsertResult> srList = Database.upsert(lstzb, false);

    // Iterate through each returned result

    for (Database.upsertResult sr : srList) {
        if (sr.isSuccess()) {
            // Operation was successful, so get the ID of the record that was processed
            System.debug('Successfully updated child email. child ID: ' + sr.getId());
        }
        else {
            // Operation failed, so get all errors                
            for(Database.Error err : sr.getErrors()) {
                System.debug('The following error has occurred.');                    
                System.debug(err.getStatusCode() + ': ' + err.getMessage());
                System.debug('child fields that affected this error: ' + err.getFields());
            }
        }
    }
        
    }
    global void finish(Database.BatchableContext BC)
  {
      Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {emails};
            mail.setToAddresses(toAddresses);
            mail.setSubject('Apex Batch Job is done');
            mail.setPlainTextBody('The batch Apex Job Processed Successfully');
            Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
  }

}

17 comments:

  1. Excellent blog admin, share more programming with results.
    Salesforce Training

    ReplyDelete
  2. I haven't seen a blog with more examples like this. Kudos to the admin.

    ReplyDelete
  3. Wonderful blog Mr.Venkataramana .covered almost every luke and corner of batch class. is there any blog of yours in queueable apex already ?

    ReplyDelete
  4. A very helpful and informative blog.. Everything related to Batch Apex is covered

    ReplyDelete
  5. All doubts we have on batch apex can be cleared out from here... Thanks a lot Venkat

    ReplyDelete
  6. Great blog It Helps to all in the Salesforce Certification Training and Interviews. Thanks for Sharing the Information.

    ReplyDelete
  7. Hiiii....Thanks for sharing Great information....Good post...Keep move on...
    Salesforce Training in Hyderabad

    ReplyDelete
  8. Excellent explanation...more information on this blog..no need to go other blogs

    ReplyDelete
  9. I tried calling future method from finish method of batch class. It says Too many Future calls.
    PFB code. Can you tell if something wrong I am doing.

    Batch Class:

    global class MyBatchClass implements Database.Batchable, Database.Stateful {

    public integer count = 0;
    public integer batchCount = 0;

    public List listOfAccounts ;

    global Database.QueryLocator start(Database.BatchableContext bc){
    System.debug('start invoked');
    String query = 'SELECT Id, Name FROM Account';

    return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext bc, List scope){
    listOfAccounts = new List();

    System.debug('Batch No: ' + batchCount++);


    for(Account acc : scope){
    //acc.Name = 'Mr.' + acc.Name;
    //listOfAccounts.add(acc);
    //
    count++ ;
    }

    System.debug('execute invoked count value is ' + count);


    //update listOfAccounts;
    }

    global void finish(Database.BatchableContext bc){

    MyFutureMethodClass.modifyAccount();

    System.debug('Finish invoked');
    System.debug('00000000000>'+count);
    System.debug('Total batches executed: ' + batchCount);

    AsyncApexJob a = [select Id, status, NumberOfErrors, JobItemsProcessed, TotalJobItems,
    CreatedBy.Email,ExtendedStatus, JobType, ApexClassId, MethodName FROM AsyncApexJob
    where Id =:bc.getJobId()];

    System.debug('Createdby email: ' + a.CreatedBy.Email );

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddress = new String[] {a.CreatedBy.Email } ;
    mail.setToAddresses(toAddress);
    mail.setSubject('Batch Class Status ' + a.Status);
    mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems +

    ' batches with '+ a.NumberOfErrors + ' failures.'+

    '\n' + ' Apex Class:' + a.ApexClassId);

    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail} );

    }


    }


    Future class:

    public class MyFutureMethodClass {

    @future
    public static void modifyAccount(){
    List listOfAccounts = new List();

    System.debug('Future Method invoked');

    for(Account a : [Select Id, Name from Account]){

    a.Name = a.Name + 'Future';

    }

    }

    }

    ReplyDelete
  10. Great Blog..it has cleard all the doubts of batch and future methods

    ReplyDelete
  11. good information of batch apex

    ReplyDelete
  12. This post is really nice and informative.
    Sales Force CPQ Online Training Hyderabad
    Visit us: sales force CPQ Online Training

    ReplyDelete
  13. one of the best blog of batch class

    ReplyDelete