I stumbled upon a strange Batch Apex issue today. My batch job was going well in few SFDC orgs; after deploying the same to a new org it stopped working completely. I just see, “First error: Attempt to de-reference a null object” in debug and apex logs. After adding a lot of debug statements in both start() and execute() method, I found that “execute(Database.BatchableContext BC, List<Sobject> scope)” is never called after creating QueryLocator in start().
So, finally after searching on dev forums found this solution; it says don’t query NULL columns/fields in initial SOQL i.e. the soql string given to QueryLocator in start(). This really fixed my problem.
So to put it all together in a clean way. We usually have batch jobs written in following manner
global class MyCoolBatch implements Database.Batchable<Sobject>{
global Database.QueryLocator start(Database.BatchableContext BC){
String query = 'Select X, Y, Z From Contact WHERE ...';
Database.Querylocator qr = Database.getQueryLocator(query);
return qr;
}
global void execute(Database.BatchableContext BC, List<Sobject> scope){
// process the records in scope, as per biz logic
}
global void finish(Database.BatchableContext BC){}
}
Here we are querying fields X, Y and Z from Contact; in case any of X,Y or Z are NULL in any records, you will face the “First error: Attempt to de-reference a null object” issue. Solutions to this problem are
- Add null checks in SOQL, for fields that could go NULL for ex. X != null. This might not be possible, as this will for sure change the records returned from the SOQL.
- Don’t query the fields that could possibly be NULL in start() method, re-query those in “execute(Database.BatchableContext BC, List<Sobject> scope)” method. This should be safer, as execute() will always be working on smaller subset of records for ex. 200 records.
I used the later option i.e. don’t query NULLABLE fields in start() & re-query those fields in execute() method, this fixed the problem !
5 comments:
Hi My requirement is to update Parent Object field ( i.e opportunity field) based on change in child records.
hereis my code so far kindly assist
my id is adiludm@gmail.com
--------------------------------------
global class updateOpportunityStage implements Database.Batchable,Schedulable{
global string query ;
global updateOpportunityStage(){
Query = 'Select Name,Id from BigMachines__Quote__c' ;
}
global database.querylocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
global void execute(SchedulableContext SC){
updateOpportunityStage stg = new updateOpportunityStage();
database.executebatch(stg);
}
global void execute(Database.BatchableContext BC, List scope){
// List oppList = new List() ;
for(sObject s : scope){
BigMachines__Quote__c quote = (BigMachines__Quote__c)s;
// System.debug('Adil'+quote);
List liOppIds;
for (BigMachines__Quote__c bmq :[ SELECT id,BigMachines__Status__c from BigMachines__Quote__c where BigMachines__Status__c = '*unison*' and BigMachines__Is_Primary__c = true ])
{
//collect opportunity Ids in a list
liOppIds.add(bmq.BigMachines__Opportunity__c);
}
//query all the opportunities in a single query
List opp = [select id, StageName from Opportunity where id in :liOppIds];
for ( Opportunity opps : opp)
{
opps.StageName = 'Closed Won' ;
}
//update all opportunities in a single DML
update opp;
}
}
global void finish(Database.BatchableContext BC){}
}
Adil, Can you please share the error logs also, with line numbers and everything.
Hi Abhinav
Here is my modified code. Now I dont get that erorr but there is only one record processed.
global class updateOpportunityStage implements Database.Batchable,Schedulable{
global string query ;
global updateOpportunityStage(){
Query = 'Select Id,BigMachines__Status__c from BigMachines__Quote__c' ;
}
global database.querylocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
global void execute(SchedulableContext SC){
updateOpportunityStage stg = new updateOpportunityStage();
database.executebatch(stg);
}
global void execute(Database.BatchableContext BC, List scope){
Set liOppIds = new Set();
//List oppList = new List() ;
for(sObject s : scope){
BigMachines__Quote__c quote = (BigMachines__Quote__c)s;
// System.debug('Adil'+quote);
if(quote.BigMachines__Status__c == 'unison' && quote.BigMachines__Is_Primary__c == true)
liOppIds.add(quote.BigMachines__Opportunity__c);
}
//query all the opportunities in a single query
List opp = new List();
opp = [select id, StageName from Opportunity where id in :liOppIds and stagename != 'Closed Won'];
for ( Opportunity opps : opp)
{
opps.StageName = 'Closed Won' ;
}
//update all opportunities in a single DML
if(opp.size() > 0)
update opp;
}
global void finish(Database.BatchableContext BC){}
}
here is my debug log
23.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WORKFLOW,INFO
12:00:01.025 (25595000)|EXECUTION_STARTED
12:00:01.025 (25635000)|CODE_UNIT_STARTED|[EXTERNAL]|01pV00000000KdY|updateOpportunityStage
12:00:01.050 (50925000)|SYSTEM_METHOD_ENTRY|[15]|BatchableContextImpl.BatchableContextImpl()
12:00:01.050 (50952000)|SYSTEM_METHOD_EXIT|[15]|BatchableContextImpl
12:00:01.051 (51996000)|METHOD_ENTRY|[1]|01pV00000000KdY|updateOpportunityStage.updateOpportunityStage()
12:00:01.052 (52014000)|METHOD_EXIT|[1]|updateOpportunityStage
12:00:01.052 (52084000)|SYSTEM_METHOD_ENTRY|[11]|Database.getQueryLocator(String)
12:00:01.054 (54864000)|SOQL_EXECUTE_BEGIN|[11]|Aggregations:0|Select Id,BigMachines__Status__c from BigMachines__Quote__c
12:00:01.195 (195089000)|SOQL_EXECUTE_END|[11]|Rows:91
12:00:01.195 (195134000)|SYSTEM_METHOD_EXIT|[11]|Database.getQueryLocator(String)
12:00:01.203 (203187000)|SYSTEM_METHOD_ENTRY|[7]|QueryLocatorIterator.QueryLocatorIterator()
12:00:01.203 (203215000)|SYSTEM_METHOD_EXIT|[7]|QueryLocatorIterator
12:00:01.422 (249266000)|CUMULATIVE_LIMIT_USAGE
12:00:01.422|LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 0 out of 200
Number of query rows: 0 out of 50000
Number of SOSL queries: 0 out of 20
Number of DML statements: 0 out of 150
Number of DML rows: 0 out of 10000
Number of script statements: 1 out of 1000000
Maximum heap size: 0 out of 6000000
Number of callouts: 0 out of 0
Number of Email Invocations: 0 out of 10
Number of fields describes: 0 out of 100
Number of record type describes: 0 out of 100
Number of child relationships describes: 0 out of 100
Number of picklist describes: 0 out of 100
Number of future calls: 0 out of 0
12:00:01.422 (249266000)|CUMULATIVE_LIMIT_USAGE_END
12:00:01.249 (249318000)|CODE_UNIT_FINISHED|updateOpportunityStage
12:00:01.249 (249332000)|EXECUTION_FINISHED
Its not coming for me Adil, I tried running with an Object with empty fields.
I would suggest one thing, try changing Apex class versions to latest or backwards. This issues seems to be coming randomly
Here is the code :
global class updateOpportunityStage implements Database.Batchable,Schedulable{
global updateOpportunityStage(){
}
global database.querylocator start(Database.BatchableContext BC){
return Database.getQueryLocator('Select Name,Id from Specimen__c');
}
global void execute(SchedulableContext SC){
updateOpportunityStage stg = new updateOpportunityStage();
database.executebatch(stg);
}
global void execute(Database.BatchableContext BC, List scope){
System.debug(LoggingLevel.INFO, '>>> ' + scope );
}
global void finish(Database.BatchableContext BC){}
}
Post a Comment