function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Ken KoellnerKen Koellner 

Getting Iterator to work with Batchable

I'm trying to write a Batchable that uses an Iterable.  I'm getting one error.  The example in the documentation isn't much help as it declares the Iterable without a type and I can't even get that to compile.

 

The issue in in the assignment of myIter in the start() method.  I get a runtime exception on that line. 

 

global class KenTestBatch implements Database.batchable<Transaction__c> { 
   /*
   	KenTestBatch batcher = new KenTestBatch();
    	Id procId = Database.executeBatch(batcher,1);
    	System.debug('ProcId ' + procId);
   */
   global Iterable<Transaction__c> start(Database.BatchableContext info){
   		Iterable<Transaction__c>myIter=(Iterable<Transaction__c>)new KenTestCustomIterable(); 
       return  myIter;
   }     

   global void execute(Database.BatchableContext info, List<Transaction__c> scope){

       for (Transaction__c tran : scope) { 

		// Do processing here.

       } 
   }     

  global void finish(Database.BatchableContext info){     
  } 
}

global class KenTestCustomIterable implements Iterator<Transaction__c>{ 

   List<Transaction__c> tranList {get; set;} 
   Integer i {get; set;} 

   public KenTestCustomIterable() { 
       tranList =        [SELECT Id, Back_Office_Order__c
       						FROM Transaction__c 
       						limit 100]; 
       i = 0; 
   }   

   global boolean hasNext(){ 
       if(i >= tranList.size()) {
           return false; 
       } else {
           return true; 
       }
   }    

   global Transaction__c next(){ 
       i++; 
       return tranList[i-1]; 
   } 
}

 

crop1645crop1645

Ken, I looked at a similar set of code I wrote to do batchable on a CSV file and I saw that I did this; Hopefully this can help. It is part of a much larger set of code so it won't compile directly for you but you can see how I broke the problem down into bits

 

//  -----------------------------------------------------------
//  Custom iterable -- implements iterator() method 
//  -----------------------------------------------------------
global class CsvRowIterable implements Iterable<CsvRowIterator.CsvRow> {
  private   String    body;              // the attachment body (String) that is the CSV
  global CsvRowIterable(String body) {
    this.body    = body;
  }
  global Iterator<CsvRowIterator.CsvRow> iterator() {    // returns an object that implements hasNext() and next(); returning one CsvRow on the next() call
    return new CsvRowIterator(this.body);
  } 
}

 

//  -----------------------------------------------------------
  //  Custom iterator on a CSVRow textBody -- implements hasNext() and next()
  //  -----------------------------------------------------------
  global class CsvRowIterator implements Iterator<CsvRow>{
    String   body;
    CsvReader    csvRdr;
    CsvRow      csvRow;
    public CsvRowIterator(String body){
      this.body    = body;
      this.csvRdr    = new CsvReader(this.body,true,null);
        }
    global boolean hasNext(){
      this.csvRow  = new CsvRow(csvRdr.readLine());
      if (this.csvRow.get() == null)  return false;
      System.debug(FlowControl.getLogLevel(),'Iterator fetched csvRow:' + this.csvRow.get());
      return true;
    }
    global CsvRow next(){
      return this.csvRow;
    }
    
    //  ----------------------------------------------------------
    //  Inner class: CsvRow - one CsvRow parsed from body
    //  ----------------------------------------------------------
    global class CsvRow {
      private List<String>  tokenList;
      public CsvRow (String[] tokenList) {
        this.tokenList = tokenList;
      }
      public List<String> get() {return this.tokenList;}
    }
  }

 

  //  ---------------------------------------------------------------------
  //  start - create the iterable that in turn provides an iterator
  //  ---------------------------------------------------------------------  
  global Iterable<CsvRowIterator.CsvRow> start(Database.BatchableContext batchContext){
    Util.debugbanner('start(), create iterable for CSV');
    try {
      return new CsvRowIterable(this.csvBody);
    }
    catch (Exception e) {
      addToLog('Exception ...  step: start()' + ' . Error ' + e.getMessage() + ' line# ' + e.getLineNumber());
      return null;
    }
  }

 

 

Prashant TiwariPrashant Tiwari

Hi Ken,

 

Update start method of batch class As :

 

global Iterable<Transaction__c> start(Database.BatchableContext info) {
 Return new KenTestCustomIterable(); 

}

 

Let me know if still facing the issue.

 

mark it as solved if the solution worked as it might help others..!!

 

Thanks,

 

Prashant Tiwari

Ken KoellnerKen Koellner

PT,

 

That won't compile, I get-- : Return value must be of type: Iterable<SOBJECT:Transaction__c>   


 

One solution that does work is to add another class that wraps the custom iteratable.  (In the manual, the analogous class is called "foo"; a metasyntacic symbol normally used for inconsequential variables.)

 

See the KenTestIterableWrapper class now included in my example.  I'm not sure if there is a way to simplify it by making the functionality of the Wrapper class a method inside the KenTestCustomerIterator class.  But in any case, the example below now compiles and executes.

 

global class KenTestBatch implements Database.batchable<Transaction__c> , Database.AllowsCallouts{ 
   /*
      KenTestBatch batcher = new KenTestBatch();
      Id procId = Database.executeBatch(batcher,1);
      System.debug('ProcId ' + procId);
   */
   global Iterable<Transaction__c> start(Database.BatchableContext info){
            return new KenTestIterableWrapper(); 
   }     

   global void execute(Database.BatchableContext info, List<Transaction__c> scope){

       for (Transaction__c tran : scope) { 

            // DO WORK HERE.

       } 
   }      

  global void finish(Database.BatchableContext info){     
  } 
}

global class KenTestCustomIterable implements Iterator<Transaction__c>{ 

   List<Transaction__c> tranList {get; set;} 
   Integer i {get; set;} 

   public KenTestCustomIterable() { 
       tranList =        [SELECT Id, Back_Office_Order__c
                                          FROM Transaction__c 
                                          limit 100]; 
       i = 0; 
   }   

   global boolean hasNext(){ 
       if(i >= tranList.size()) {
           return false; 
       } else {
           return true; 
       }
   }    

   global Transaction__c next(){ 
       i++; 
       return tranList[i-1]; 
   } 
}

global class KenTestIterableWrapper implements iterable<Transaction__c>{

   global Iterator<Transaction__c> Iterator(){
      return new KenTestCustomIterable();
   }

}

 

Ankit Rustagi 19Ankit Rustagi 19
@crop1645 and other forum members :-
I have following 2 classes and third class is throwing the error; can anyone identify; why it is throwing the error :-

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
global class OppHistMonthlyUpload_Iterator implements Iterator<Opportunity_Trend_History__c>{ 

   List<Opportunity_Trend_History__c> oppTrendHistList {get; set;} 
   Integer i {get; set;} 

   public OppHistMonthlyUpload_Iterator(List<Opportunity_Trend_History__c> othlist){
       if(oppTrendHistList == null){
           oppTrendHistList=new List<Opportunity_Trend_History__c>();
       }
       oppTrendHistList.addAll(othlist);
        
       i = 0; 
   }   

   global boolean hasNext(){ 
       if(i >= oppTrendHistList.size()) {
           return false; 
       } else {
           return true; 
       }
   }    

   global Opportunity_Trend_History__c next(){ 
       i++; 
       return oppTrendHistList[i-1]; 
   } 
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
global class OppHistMonthlyUpload_Iterable implements iterable<Opportunity_Trend_History__c>{

   List<Opportunity_Trend_History__c> oppTrendHistList = new List<Opportunity_Trend_History__c>(); 
   
   public OppHistMonthlyUpload_Iterable(List<Opportunity_Trend_History__c> othlist){
        this.oppTrendHistList.addAll(othlist);       
   }
    
   global Iterator<Opportunity_Trend_History__c> Iterator(){
      return new OppHistMonthlyUpload_Iterator(oppTrendHistList);
   }
}
***********************************************************************************************************************************************************************
Error: Compile Error: Class DMLInsertForMoreThan10000Rows_BATCH must implement the method: System.Iterable<SObject> Database.Batchable<SObject>.start(Database.BatchableContext)
***********************************************************************************************************************************************************************
global class DMLInsertForMoreThan10000Rows_BATCH implements 
    Database.Batchable<sObject>, Database.Stateful {
    
    global Map<Id, Opportunity_Trend_History__c> mapOfOppIdNOppTrendHist = new Map<Id, Opportunity_Trend_History__c>();
    
    global DMLInsertForMoreThan10000Rows_BATCH(Map<Id, Opportunity_Trend_History__c> mapOfOppIdNOppTrendHist){
        mapOfOppIdNOppTrendHist.putAll(mapOfOppIdNOppTrendHist);
    }

    global Iterable<Opportunity_Trend_History__c> start(Database.BatchableContext bc) {
        OppHistMonthlyUpload_Iterable abc = new OppHistMonthlyUpload_Iterable(mapOfOppIdNOppTrendHist.values());
        return abc;
    }

    global void execute(Database.BatchableContext bc, List<Opportunity_Trend_History__c> scope)
    {
        insert scope;        
    }    

    global void finish(Database.BatchableContext bc){
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            mail.setTargetObjectId(UserInfo.getUserId());
            mail.setSaveAsActivity(false);
            mail.setSenderDisplayName('Batch Processing');
            mail.setSubject('Batch Process Completed');
            mail.setPlainTextBody('Batch Process has completed');
            Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });        
    }    

}