Today’s post is on a pattern for batch classes that has kind of solidified in the last couple of years, at least from my perspective!

Its been a hectic couple of weeks and along with the usual mix of client meetings, workshops, development and delivery we’re prepping for Dreamforce ’18.  With our ever increasing suppliers and customer community and the busy lives that everyone is leading – we’re looking forward to catching up in the relative “quiet” in San Francisco.

Firstly, create an iterator class …. Okay so here’s the first part of the pattern that everyone gets confused about, why use an iterator? Well …. My response to this question is a little vague but comes down to two factors;

The iterator separates the underlying logic of what triggers your batch from the means of actually processing the batch, so this means that the governor limits are managed more efficiently from your batch perspective;

The iterator does not need to return a list of objects, therefore you can return anything numbers, list of class, it doesn’t matter what ….

Heres a sample that I used to enumerate through the Alphabet, why you ask …. Well in this case the webservice we are calling needed at least one character before it would return a set of data (starting with that character).

 

global class Alphabet implements Iterator<String>, Iterable<String>{

private string Digits = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’;

private Integer i {get; set;}

 

public Alphabet(){

i = 0;

}

 

public Alphabet(boolean IncludeLowercase, boolean IncludeNumbers){

this();

if ( includeLowercase ) Digits += Digits.toLowerCase();

if ( includeNumbers ) Digits += ‘0123456789’;

}

 

global boolean hasNext(){

return ( i>=0 && i < Digits.length());

}

 

global String last(){

i=Digits.length()-1;

return next();

}

 

global String first(){

i=0;

return next();

}

 

global String next(){

i++;

return Digits.substring(i-1,i);

}

 

global Iterator<String> Iterator(){

return new Alphabet();

}

}

 

 

Here’s another example, this one we used to run a set of merge’s over a set of contact data.

 

public class MergeRecordsIterable implements Iterator<AggregateResult>, Iterable<AggregateResult>{

 

public List<AggregateResult> mergeRecords = null;

public Integer i = 0;

 

public MergeRecordsIterable(){

mergeRecords = [SELECT FirstName, LastName, Email, AccountId FROM Contact GROUP BY FirstName, LastName, Email,AccountId  HAVING COUNT(Id)>1];

}

 

public boolean hasNext(){

return ( mergeRecords != null && i < mergeRecords.size());

}

 

public AggregateResult next(){

i++;

return mergeRecords[i-1];

}

 

public Iterator<AggregateResult> Iterator(){

return new MergeRecordsIterable();

}

}

 

In both cases the logic is pretty obvious, we implement the hasNext() and Next() functions, its worthwhile noting that these functions do not actually have to enumerate over a complete list ….

 

public class LongRunningIterable implements Iterator<integer>, Iterable<integer>{

 

public Boolean continueRunning = true;

private integer iterationCount = 0;

 

public LongRunningIterable (){

}

 

public boolean hasNext(){

return ( continueRunning );

}

 

public integer next(){

iterationCount++;

return iterationCount;

}

 

public Iterator< LongRunningIterable > Iterator(){

return new LongRunningIterable ();

}

}

 

Okay so what’s the point of this you well may ask.  Well using this example we can execute batch code until some arbitrary number is reached, want to run something for 1 to 1,000,000 the above will do so!

So that’s the first part of the batchable pattern, define an iterator and use it.  The second part is to implement the actual batch code.  For this one I’m going to use the Contact merge example.

 

public class MergeRecordsBatch implements Database.Batchable<AggregateResult>{

 

// Calling Iterator to query records

public Iterable<AggregateResult> start(Database.BatchableContext BC){

return new MergeRecordsIterable();

}

 

// Logic to merge duplicate records

public void execute(Database.BatchableContext BC,List<AggregateResult> listOfCont){

 

for(AggregateResult cont : listOfCont){

 

List<Contact> contList = [SELECT FirstName,LastName,Email,AccountId FROM Contact WHERE FirstName =: String.valueOf(cont.get(‘FirstName’)) AND LastName =: String.valueOf(cont.get(‘LastName’)) AND Email =: String.valueOf(cont.get(‘Email’)) AND AccountId =: String.valueOf(cont.get(‘AccountId’)) ORDER BY CREATEDDATE DESC LIMIT 3];

List<Contact> mergeContList = new List<Contact>();

for(Contact a : contList){

if(contList.get(0).Id <> a.Id){

mergeContList.add(a);

}

}

 

try{

MERGE contList.get(0) mergeContList;

}

catch(Exception ex){

System.debug(‘Merge Exception’ + ex);

}

}

}

 

public void finish(Database.BatchableContext BC){

 

}

 

public static void execute(){

Database.executeBatch( new MergeRecordsBatch(), 10);

}

}

 

How simple is that right?  Using the above every contact is merged, well not quite as we have the limit on the number of records we can merge (3), to solve this problem we need to add one small tweak in the finish method.

 

public void finish(Database.BatchableContext BC){

if(new MergeRecordsIterable().hasNext()){

MergeRecordsIterable.execute();

}

}

 

All this does is call our iterable and check if there is anything to actually merge, if there is kick off another instance of the merge process.

Hope the above code makes sense, next post I’m going to convert the above code to make it a generic merge for any object – just because I can!

 

– Mark

Leave a Reply

Your email address will not be published. Required fields are marked *