I am under the suspicion that the execute method in a batch apex class does not get called when the scope is null. Is this true? When the scope returns null I still want the execute method to be called.
3 Answers
The execute()
method will not be called if your start
method returns either null
or an empty iterable. So for example if you implement Database.Batchable<sObject>
and your Database.getQueryLocator(query)
returns an empty result set, the execute()
method will not run.
-
2+1, though, in simpler terms, "execute will not run if there's nothing to do."– sfdcfoxOct 24, 2019 at 15:49
-
-
-
No, but you can move the logic currently into your
execute()
method to another Apex Class, and then in your batchstart()
method, if you can determine that your query or your iterable collection will be empty, then you can call that other Apex class Oct 24, 2019 at 17:58 -
1@Bahman.A you would not return null. Just an empty iterable object. Dec 31, 2020 at 15:39
The system will never run an execute method if there's nothing to run. Also be aware that null values are stripped from the list of elements to process. As a workaround, you could consider using a Wrapper class and returning at least one value. For example:
public Wrapper[] start(Database.BatchableContext context) {
Wrapper[] results = new Wrapper[0];
Wrapper temp = new Wrapper();
for(sObject record: ...) {
temp.records.add(record);
if(temp.records.size() == 100) {
results.add(temp);
temp = new Wrapper();
}
}
// add the last (partial) batch
results.add(temp);
return results;
}
At this point, you've constructed the code in a way that will force at least one iteration of execute, even if there's nothing to do.
However, this solution won't work if you expect you may get more than 50,000 records. You might also be able to use a hybrid solution by returning Iterable<Object>
and use a Wrapper if there's less than X records, or a Database.QueryLocator
otherwise.
If you are using a query locator implementation, you could force execute to be run once by ensuring start:
- Runs the exact same query WHERE against the same object but as a COUNT.
- If the count is 0 then explicitly call execute with an empty scope
- Return the required query locator
This only consumes one additional query per batch execution, and one additional row in start (which is not relevant to the limits applied when execute is called anyway since start/execute/finish are called in different transactions).