The Apex Developer Guide compares SOQL for Loops against Standard SOQL Queries, and highlights certain potential advantages that developers may need or want to leverage.
But putting the SOQL directly into the for statement can be ugly and hard to read, especially if you are querying for many fields, include aggregations, where clauses.
For a very contrived example (I don't actually want to count contacts!), I find it is more difficult to read this:
Map<Id, Integer> contactCountByAccountIdMap = new Map<Id, Integer>();
for (Account account : [SELECT Id, Name, (SELECT Id, Name FROM Contacts)
FROM Account WHERE Id IN :accountIdSet]) {
List<Contact> contactList = account.Contacts;
contactCountByAccountIdMap.put(account.Id, account.Contacts.size());
}
than
List<Account> accountsWithContactList = [
SELECT Id, Name, (SELECT Id, Name FROM Contacts)
FROM Account
WHERE Id IN :accountIdSet
];
Map<Id, Integer> contactCountByAccountIdMap = new Map<Id, Integer>();
for (Account account : accountsWithContactList ) {
List<Contact> contactList = account.Contacts;
contactCountByAccountIdMap.put(account.Id, account.Contacts.size());
}
Also (without adding the complexity of the loan pattern), you can't use a DAO/Selector object to invert control and mock out the dependency for testing.
So, if I want to leverage SOQL-for-loops, I can't do it like this:
public class DOA_AccountSelector {
public List<Account> selectById(Set<Id> accountIdSet) {
return [
SELECT Id, Name, (SELECT Id, Name FROM Contacts)
FROM Account
WHERE Id IN :accountIdSet
];
}
}
Map<Id, Integer> contactCountByAccountIdMap = new Map<Id, Integer>();
for (Account account : this.accountSelector.selectById(accountIdSet) ) {
List<Contact> contactList = account.Contacts;
contactCountByAccountIdMap.put(account.Id, account.Contacts.size());
}
And I really wouldn't want to do this:
public interface AccountBorrowerIntf {
void execute(Account account);
}
public class LOAN_ContactCounter implements LOAN_AccountBorrowerIntf {
public Map<Id, Integer> contactCountByAccountIdMap {get; set;}
public LOAN_ContactCounter() {
this.contactCountByAccountIdMap = new Map<Id, Integer>();
}
public void execute(Account account) {
List<Contact> contactList = account.Contacts;
contactCountByAccountIdMap.put(account.Id, account.Contacts.size());
}
}
public class AccountSelector {
public void borrowAccounts(AccountBorrowerIntf accountBorrower, Set<Id> accountIdSet) {
for (Account account : [
SELECT Id, Name, (SELECT Id, Name FROM Contacts)
FROM Account
WHERE Id IN :accountIdSet
]) {
accountBorrower.execute(account);
}
}
}
ContactCounter contactCounter = new ContactCounter();
this.accountSelector.borrowAccounts(contactCounter, accountIdSet);
Map<Id, Integer> contactCountByAccountIdMap = contactCounter.contactCountByAccountIdMap;
So, I'm wondering if there is any functional equivalent which would allow me to extract the query but allow the code to behave the same way?