top of page
Writer's pictureD. Dewangan

How to use more than one sObject using Iterable<sObject>

Updated: Oct 26, 2023



Wonder, we got the requirement to write a Batch Apex class where sObject type is more than one. As a result, Iterable<sObject> must be used there. I'll show you how to write a generic batch class that accepts sObjects, their field names, and field values, and updates them using Iterable<sObject>.


Key points:

Iterable<sObject> use over Database.getQueryLocator are as follows:

● To deal with more than one sObject in a single batch class.

● To perform SOSL queries.

● To pre - perform complex logic.

● Any type of set of data you can think of, including mathematical sequences, extended search results from a service like Twitter, and other massive data sources, can be iterated through as long as your iterator returns the right values for hasNext and next.

● Work can get accomplished within the governor limits.


Let's dive deeper into the iterable interface methods.


Iterable<sObject> in salesforce

An iterator traverses through every item in a collection. The Iterator interface has the following instance methods:

Name

Returns

Description

hasNext

Boolean

Returns true if there’s another item in the collection being traversed, false otherwise.

next​

Any type​

Returns the next item in the collection.

If you don’t want to use a custom iterator with a list, but instead want to create your data structure, you can use the Iterable interface to generate the data structure.

The Iterable interface has the following method:

Name

Returns

Description

​iterator

Iterator class

Returns a reference to the iterator for this interface.

next

Any type

Returns the next item in the collection.

We are aiming to develop a generic batch class that accepts more than one sObject type, their field names, and field values, and updates them using Iterable<sObject>.

Steps:

  1. Create a class implementing Iterable interface: GenericIterable.apxc

Here we will take the inputs from the batch class that is to be further passed to the Iterator class.


2. Create a class implementing the Iterator interface: GenericIterator.apxc

Here we will handle the logic to be performed on getting specific sObject while iterating through the list<sObject>.

For example, While traversing list<sObject>, if the current sObject is Account then, populate the name field as ‘Avenoir Technologies Pvt.Ltd.’ else if the current sObject is Contact then, populate the LastName as ‘Avenoir Pvt. Ltd.’.


3. Create a batch class: sObjectProcessor.apxc

● Start method:

○ Return type: Iterable<sObject>.

○ Return: GenericIterable.

● Execute method:

○ Here we will update the list<sObject>.

● Finish method:

○ Here we may perform postprocessing.

● Constructor: This will iniitialize the inputs as follows to be processed:

  • List<sObject> objects: this will specify the sObjects (1 or more than one sObject types) to be processed.

  • String object1FieldName, object1FieldValue, object2FieldName, object2FieldValue which are the respective field name and field value that is to be updated.


GenericIterable.apxc

public class GenericIterable implements Iterable<sObject>{
    
    //This class aims to input parameters  from the batch class, wrap them and pass it to Iterator 
       //class for custom processing
 
    List<sObject> objects;
    String object1, object2;
    String object1FieldName, object1FieldValue;
    String object2FieldName, object2FieldValue;
    
    //Constructor 
    public GenericIterable(
        String object1,
        String object1FieldName,
        String object1FieldValue,
        String object2,
        String object2FieldName,
        String object2FieldValue,
        List<sObject> objects
    ) {
    	  this.object1 = object1;
        this.object1FieldName = object1FieldName;
        this.object1FieldValue = object1FieldValue;
        this.object2 = object2;
     	  this.object2FieldName = object2FieldName;
        this.object2FieldValue = object2FieldValue;
        this.objects = objects;
    }
    
    //This method passes the inputs to the Iterator class for custom processing and returns the 
        //processed list<sObject>
    public Iterator<sObject> Iterator() { 
        return (
           new GenericIterator(
                object1,
                object1FieldName,
                object1FieldValue,
                object2,
                object2FieldName,
                object2FieldValue,
        	    objects
        ));
    } 
}


GenericIterator.apxc


public class GenericIterator implements Iterator<sObject>{
    
       //This class aims to take input parameters and do custom processing according to the current 
       //sObjectType and return the processed list<sObject>

    Integer count = 0;    
    String object1, object2;
    String object1FieldName, object1FieldValue;
    String object2FieldName, object2FieldValue;
    List<sObject> objects = new list<sObject>();
    
    //Constructor 
    public GenericIterator(
        String object1,
        String object1FieldName,
        String object1FieldValue,
        String object2,
        String object2FieldName,
        String object2FieldValue,
        List<sObject> objects
    ) {
    	  this.object1 = object1;
        this.object1FieldName = object1FieldName;
        this.object1FieldValue = object1FieldValue;
        this.object2 = object2;
     	  this.object2FieldName = object2FieldName;
        this.object2FieldValue = object2FieldValue;
        this.objects = objects;        
    }
    
    //This method of Iterator class returns true if there’s another item in the collection being traversed,
       //false otherwise.
    public Boolean hasNext() {
        return (count < objects.size());
    }
    
    //This method returns the next item in the collection [this item can be of any type]
    public sObject Next() {
       if (hasNext()) {
           //If the current sObject is object1 (sObjectType1) then, populate the object1fieldName with 
                      //respective object1fieldValue
           if (
               objects[count].getsObjectType() ==  
               Schema.getGlobalDescribe().get(object1).newSObject().getsObjectType()
           ) {
               sObject object1Instance = objects[count];
               object1Instance.put(object1FieldName, object1FieldValue);
           }
           
           //If the current sObject is object2 (sObjectType2) then, populate the object2fieldName with 
                      //respective object2fieldValue
           else if(
               objects[count].getsObjectType() ==
               Schema.getGlobalDescribe().get(object2).newSObject().getsObjectType()
           ) {
               sObject object2Instance = objects[count];
               object2Instance.put(object2FieldName, object2FieldValue);               
           }
           //returns the next item in the collection
           return objects[count++];
       } else {
           throw new NoSuchElementException(System.Label.NoSuchElementException);
       }
    }
}

sObjectProcessor.apxc


public class sObjectProcessor implements Database.Batchable<sObject> {
    
    List<sObject> objects = new list<sObject>();
    String object1, object2;
    String object1FieldName, object1FieldValue;
    String object2FieldName, object2FieldValue;
    
       //constructor to initialize the object 
    public sObjectProcessor(
        String object1,
        String object1FieldName,
        String object1FieldValue,
        String object2,
        String object2FieldName,
        String object2FieldValue,
        List<sObject> objects
    ) {
    	  this.object1 = object1;
        this.object1FieldName = object1FieldName;
        this.object1FieldValue = object1FieldValue;
        this.object2 = object2;
     	  this.object2FieldName = object2FieldName;
        this.object2FieldValue = object2FieldValue;
        this.objects = objects;
    }
    
    public Iterable<sObject> start(Database.BatchableContext bc) {
        //this will return the processed list<sObject>
        return (
            new GenericIterable(
                object1,
                object1FieldName,
                object1FieldValue,
                object2,
                object2FieldName,
                object2FieldValue,
        	    objects
        ));
    }
    
    public static void execute (Database.BatchableContext bc, list<sObject> scope) {
    //update the list<sObject>    
    try {
            update scope;
        }
        catch (exception e) {
            System.debug(e.getMessage() + e.getStackTraceString());
        }
    }
    
    public void finish(Database.BatchableContext info) {
    	//do post processing here
    }
}

Custom Labels Used

CUSTOM LABEL

VALUE

System.Label.NoSuchElementException

Iterator has no more elements.


So, now let's test the code. First, let's test the data and add it to the sObject list, then pass the desired sObject names and their desired field name and field value to be updated.

List<sObject> objects = new List<sObject> ();
for(account a : [select id, name from account where name like 'test%' limit 2]) {
    objects.add(a);
}
for(contact a : [select id, email from contact where lastname like 'test%' limit 2]) {
    objects.add(a);
}
System.debug(objects);
Database.executeBatch(
    new sObjectProcessor(
        'Account', 'Name', 'Avenoir Technologies Pvt. Ltd.',
        'Contact', 'Lastname', 'Avenoir Pvt. Ltd.',
         objects
));

Input List:

Output List:


Happy!! to help you add to your knowledge. You can leave a comment to help me understand how the blog helped you. If you need further assistance, please contact us. You can click "Reach Us" on the website and share the issue with me.



Blog Credit:

D. Dewangan

Salesforce Developer

Avenoir Technologies Pvt. Ltd.


Reach us: team@avenoir.ai


Are you in need of Salesforce Developers?

2,035 views0 comments

© 2024 by Avenoir Technologies Pvt. Ltd.

bottom of page