0

I'm trying to execute the same batch class for a series of dates and I want each batch to process after the previous one has completed. I'm not sure, from an architecture standpoint how to achieve this. I don't think I need to implement Database.Stateful as I won't be incrementing my variable in the execute method, but instead at the end.

I don't work with batch Apex too often, but I'd really like to pass a date variable to the batch class, and then, after the batch finishes with all records for that date, increment that date and process again for the next date (until I reach today).

I imagine this looks something like this, but I'm not sure of the exact methodology:

global class MyBatchClass implements Database.Batchable<SObject>{

    String queryBegin = 'some SOQL Query WHERE CreatedDate = ';

    public Date d;

    String query = queryBegin + String.valueOf(d);

    global Database.QueryLocator start(Database.BatchableContext bc){
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext bc, List<SObject> scope){
        //do stuff
    }

    global void finish(Database.BatchableContext bc){
        //queue up next one
        d += 1;
        if(d <= Date.today()){
            Database.executeBatch(new MyBatchClass(d));
        }
    }

}

In the execute method, I'll be finding records related to the records returned in the start method, but I need to grab them one date at a time. I know the above isn't quite right, but any help here is much appreciated.


*********EDIT*********

I took the advice @sfdcfox and now if I try to queue this up, I just get runaway Apex. It continues to execute an infinite number of batch jobs. I added some debug statements to the finish method to try and track the issue down, and it seems to increment the DateTime variables, but when it chains the next job, those values aren't carried over. It doesn't seem to matter if I use Database.executeBatch(this) or new MyBatchClass(startDateTime.date()).

public class MyBatchClass implements Database.Batchable<SObject>{

    DateTime startDateTime, endDateTime;

    public MyBatchClass(Date firstDate){
        startDateTime = DateTime.newInstance(firstDate, Time.newInstance(0,0,0,0));
        endDateTime = startDateTime.addDays(1);
    }

    public Database.QueryLocator start(Database.BatchableContext bc){
        return Database.getQueryLocator([SELECT Fields
                                         FROM SObject 
                                         WHERE CreatedDate >= :startDateTime
                                         AND CreatedDate < :endDateTime]);
    }

    public void execute(Database.BatchableContext bc, List<SObject> scope){
        //do stuff
    }

    public void finish(Database.BatchableContext bc){
        Date yestDay = Date.today()-1;
        startDateTime.addDays(1);
        endDateTime.addDays(1);
        if(startDateTime.date() < yestDay){
            //Database.executeBatch(this);
            //Database.executeBatch(new MyBatchClass(startDateTime.date()));
        }
    }

}

I don't think it matters, but I'm in a dev org where there is no data returned by the query locator, so nothing happens in the execute method. I'm just trying to test the chaining functionality.

  • You can give a try and post the issue what you are facing – Santanu Boral Oct 30 '16 at 22:51
2

You're like 95% there, actually. There's really just a few minor things you can optimize, like fixing your query and date-time handling. Here's a re-write:

public class MyBatchClass implements Database.Batchable<SObject> {
    DateTime startDateTime, endDateTime;
    public MyBatchClass(Date firstDate) {
        startDateTime = DateTime.newInstance(firstDate, Time.newInstance(0, 0, 0, 0));
        endDateTime = startDateTime.addDays(1);
    }
    public Database.QueryLocator start(Database.BatchableContext context) {
        return Database.getQueryLocator(
            [SELECT fields 
             FROM sObject 
             WHERE CreatedDate >= :startDateTime AND CreatedDate < :endDateTime]);
    }
    public void execute(Database.BatchableContext context, SObject[] scope) {
        // Do stuff here
    }
    public void finish(Database.BatchableContext context) {
        startDateTime = startDateTime.addDays(1);
        endDateTime = endDateTime.addDays(1);
        if(DateTime.now() > startDateTime) {
            Database.executeBatch(this);
        }
     }
}
  • Thanks @sfdcfox. A couple things: 1) This gives me a compile error: Return value must be of type: Database.QueryLocator and 2) While I won't increment the date I feed into the batch in the execute method, I do need it to be accessible in the execute method (e.g. if(someOtherDate < startDateTime){//do something interesting}, which I'm not sure if I can do with this approach. Am I missing something here? – Todd Sprinkel Oct 31 '16 at 2:34
  • @ToddSprinkel Sorry, I forgot the Database.getQueryLocator bit. As far as the second question, yes, your non-static variables will be available inside of the execute method. If you need just the date portion, you can do: if(someOtherDate < startDateTime.date()) – sfdcfox Oct 31 '16 at 2:48
  • The chaining bit doesn't seem to be working properly. Updated my question with more info. – Todd Sprinkel Oct 31 '16 at 18:05
  • Any idea as to why this is chaining infinitely? Do I need to add database.stateful? I though that was only for maintaining variables across execute batches but maybe I'm misreading the documentation. – Todd Sprinkel Nov 2 '16 at 3:30
  • @ToddSprinkel Serialization occurs when Database.executeBatch is called, so assuming you're updating the variable correctly, it shouldn't run indefinitely. The code I wrote should stop when it reaches today. – sfdcfox Nov 2 '16 at 3:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.