Enforce Governor-Limit-Safe Batch Processing
By the end of this lesson, you'll be able to:
- Write a Batch Apex class to recalculate balances for all accounts
- Explain why a full recalculation batch job is a useful safety net
- Size the batch scope appropriately for the workload
Prerequisites: "Build Bulk-Safe Transaction Triggers"
Why recalculate at all, if the trigger already keeps it correct
global class RecalculateBalancesBatch implements Database.Batchable<SObject> {
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id, Balance__c FROM Bank_Account__c]);
}
global void execute(Database.BatchableContext bc, List<Bank_Account__c> scope) {
Map<Id, Decimal> sums = new Map<Id, Decimal>();
for (AggregateResult ar : [
SELECT Bank_Account__c acc, SUM(Amount__c) total
FROM Transaction__c
WHERE Bank_Account__c IN :scope AND Type__c = 'Deposit'
GROUP BY Bank_Account__c
]) {
sums.put((Id) ar.get('acc'), (Decimal) ar.get('total'));
}
// (a full solution also subtracts withdrawal sums per account)
for (Bank_Account__c acc : scope) {
acc.Balance__c = sums.containsKey(acc.Id) ? sums.get(acc.Id) : 0;
}
update scope;
}
global void finish(Database.BatchableContext bc) {}
}
Even a well-tested trigger (Module 31) can drift from correct over time — a bulk data-load bypassing the trigger, a bug fixed after some records were already affected, or a manual data correction. A periodic recalculation job, using Module 38's exact Database.Batchable shape, is a safety net that self-heals any such drift.
This is Module 38's pattern, not a new one
The start/execute/finish structure, the Database.QueryLocator for scale, and the default 200-record scope are identical to Module 38's Batch Apex lesson — a banking system doesn't need different batch mechanics, it needs the same reliable mechanics applied to a domain where "the numbers must be right" carries more weight than a typical rollup.
Choosing a smaller batch scope deliberately
Database.executeBatch(new RecalculateBalancesBatch(), 50);
Passing 50 instead of the default 200 reduces the risk of any single batch's aggregate query hitting limits when an account has an unusually large transaction history — a deliberate, documented trade-off (slower overall job, safer per-batch execution) rather than an arbitrary number.
Exercise
As a comment, explain why a bulk data load (e.g. a data migration inserting Transaction__c records directly via the Bulk API with triggers disabled) could cause Balance__c to drift out of sync, and how this batch job fixes that.
Show hint
Think about what the trigger from Lesson 2 actually depends on to run.
Enforce Governor-Limit-Safe Batch Processing Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
A Batch Apex job recalculates every account's balance from scratch against its full transaction history — a periodic correctness safety net, built with the exact Batch Apex mechanics from Module 38.