0

I have a batch class that updates records to former once their term has expired. This portion works. The next step is to create a running list of the users who were changed and then add the list to an email. The email sends but the list is not added. I tried debugging my values to see whats null/ whats happening but the debug log isnt showing my debug lines. Can anyone tell from my code why I am not getting a list of contact names? One thing to note is that the Contact__c field on the affiliation object is a master-detail field, could that be causing the problem?

global class AffiliatedStatusBatchUpdate implements Database.Batchable<sObject> {

String email = 'trace_combs@redpathcg.com';
List<String> listOfAffiliates = new List<String>();
String contactId = '';
global Database.QueryLocator start(Database.BatchableContext bc){
   String afQuery = 'SELECT Name, npe5__EndDate__c, npe5__Contact__c, npe5__Status__c FROM npe5__Affiliation__c WHERE npe5__Status__c = \'Current\'';
    System.debug(afquery);
    return Database.getQueryLocator(afQuery);

}
global void execute(Database.BatchableContext bc, List<npe5__Affiliation__c> scope){
    // process each batch of records

    for(npe5__Affiliation__c affiliate: scope){
        if( affiliate.npe5__EndDate__c <= date.today()){
            affiliate.npe5__Status__c = 'Former';
            contactId = affiliate.npe5__Contact__c;
            System.debug(contactId);
            listOfAffiliates.add(contactId);
        }
    }
    System.debug(listOfAffiliates);
    update scope;
}
global void finish(Database.BatchableContext bc){
   String s = 'Batch Process has completed. Here is a List of Affiliate contacts who were affected: ';

    Integer i = 0;
    for(String cId : listOfAffiliates){
       i++;
      String contactName = [SELECT Full_Name__c FROM Contact WHERE Id = :contactId].Full_Name__c;
        s += String.valueOf(i) + '. ' + contactName + ', ';                        
    } 
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] {email});
mail.setReplyTo('batch@acme.com');
mail.setSenderDisplayName('Batch Processing');
mail.setSubject('Batch Process Completed');
mail.setPlainTextBody(s);

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

}

}

  • When debugging listOfAffiliates, are you actually seeing a populated list? Can you confirm that the batch job is actually finding Affiliates that are Current and have an end date <= today? Have you tried debugging npe5__EndDate__c to confirm that it is actually populated? – Roti Nov 1 '19 at 18:27
  • As mentioned above my debugs arent populating in the log after the batch class runs so I cannot tell if the list is being created. the job is finding and updating the proper affiliates though, just not saving the data to a list like I wanted. – Trace Nov 1 '19 at 18:58
  • 1
    As David as pointed out, you will need Database.Stateful. Another thing to make sure is that your debug log level is not set to ignore Debug logs. If the log level is ok, then there is an issue somewhere that is causing the if condition to not be met. But if you are seeing updates, then I'm not sure what is going on at the moment. – Roti Nov 1 '19 at 19:04
  • Your finish() method is doing soql in a loop - this will blow up if more than 100 affiliates. finish() should be bulkified – cropredy Nov 2 '19 at 18:03
  • @cropredy How would I go about bulkifying this portion of the code? – Trace Nov 4 '19 at 17:41
2

Your batch class needs to implement the Database.Stateful marker interface.

If you don't do so, your instance variables' values are not maintained from transaction to transaction. Your batch class will execute start(), finish(), and each invocation of execute() in a separate transaction, and the instance variables will be reset between each.

Database.Stateful requests that the runtime preserve these values throughout the run of your batch job:

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 is considered five transactions of 200 records each.

If you specify Database.Stateful in the class definition, you can maintain state across these transactions. When using Database.Stateful, only instance member variables retain their values between transactions. Static member variables don’t retain their values and are reset between transactions. Maintaining state is useful for counting or summarizing records as they’re processed. For example, suppose your job processed opportunity records. You could define a method in execute to aggregate totals of the opportunity amounts as they were processed.

If you don’t specify Database.Stateful, all static and instance member variables are set back to their original values.

| improve this answer | |
  • I added the mentioned implementation to my class and it is now pulling data through. We have another issue though and that is instead of adding the names of all contacts affected, it just added the name of the first contact affected over and over for each record affected. In this run I had two users who were affected, John Stockton and Finn Flan, the list returned was "1. John Stockton 2. John Stockton" not "1. John Stockton 2. Finn Flan – Trace Nov 1 '19 at 19:09
  • Its working now thanks for your help! – Trace Nov 1 '19 at 20:06
  • @Trace - also best practice to save the minimum amount of data in the stateful variables to avoid running out of heap. I'd save just the ids – cropredy Nov 2 '19 at 18:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.