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:
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?