4

I have a map variable that I am trying to pass into a batch class. This variable was created and populated in another batch class (which is why I can't just create it in the Start() method of the second batch class).

The first batch class implments Database.Stateful, in order to preserve the values I store in the map variable. Then, in the Finish() method, I call the second batch class, passing the map variable, like so:

Database.executeBatch(new BatchClass(mapVariable));

The problem I'm running into is that I can't get the second batch class to recognize that I've passed this map to it. I've tried using the this.variable assignment in the constructor class without being clear on its purpose, to no avail. Here's an idea of what that second batch class looks like now:


global class BatchClass implements Database.Batchable<sObject>{


    String query = '[some SOQL query]';


    global BatchClass(map<key, value> mapVariable) {
        this.mapVariable = mapVariable;
    }

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

    global void execute(Database.BatchableContext BC, List<sObject> scope) {
          methodWhichUsesMapVariable();
    }

    global void finish(Database.BatchableContext BC) {}

In this format, the batch class doesn't recognize mapVariable (says it does not exist). I've also tried making this batch class stateful, as well as declaring the mapVariable separately prior to the start() method, similar to the query variable, which I thought might be necessary to do the this.mapVariable assignment.

How can I make it so this Batch class can use the map variable I'm trying to pass to it? Thanks for your help and Merry Xmas/happy holidays!


EDIT:

So I got it to work (or at least save without error) by declaring the map variable before the start() method, then assigning the passed variable to it, like so:

    public static map<string, decimal> mapVariable = new map<string, decimal>();

    global BatchClass(Map<string, decimal> sameMapVariableWithDiffName) {
        mapVariable = sameMapVariableWithDiffName;          
    }

The interesting thing is, it didn't work until I specifically made the declared variable a static variable. My understanding is that even though I made this batch class stateful, static variables are reset with every batch. I don't know if this means I'm going to lose the map values after the first batch, but I'm about to find out!

If anyone knows why I could only get this to work by declaring the map static, I'd love to learn the reason!


EDIT 2:

First off, my 'solution' from EDIT 1 doesn't seem to be working (I think the map I'm using in my second batch class is actually an empty map, not what I tried to pass from the first batch class).

Anyway, in response to a request from Crop1645, here is the first part of the code for my batch class without any simplification or abstraction (which I had done in my first code example):

global class ClientMetrics_Stage6 implements Database.Batchable<sObject>, Database.Stateful {


    //Stage 6
    //Batches through the Usage Aggregate Data that was inserted in Stage 2. 
    //For each batch, pulls all* accounts' contracts and addendums in the system, determines matchings between them and the Aggregate usage data, 
    //and applies calculations using the contract data.
    //*: Not ALL accounts, actually, just those who have been marked ready for contract review, and didn't have any results in their audit field.

    //This stage takes a map passed in from stage 5, which has stored the total number of courses launched for each Account/month/product combo. This will be used by
    //calcRecMMAC() to calculate the individual record MMAC (which is derived from the ratio of courses launched in that record vs courses launched for that account/month/product)

    String query = 'SELECT ID, Account__c, Aggregate_Courses_Launched__c, Aggregate_Set__c, Country__c, Domain__c, MonthStart__c, YearStart__c, Product__c, Course_Name__c, Launch_Month__c ' + 
                    'FROM Usage_Data_Aggregates__c WHERE Aggregate_Set__c = \'General Aggregated Usage\' AND Account__r.Contracts_Reviewed__c = TRUE AND Account__r.Audit_Field_Overlapping_Contracts__c =\'\'';

    static map<string,string> productMap = new map<string,string>{'IPIMS BL Subtopic' => 'IPIMS Background Learning',
                           'IPIMS BL Topic' => 'IPIMS Background Learning',
                           'IPIMS AL' => 'IPIMS Action Learning',
                           'PO' => 'Petroleum Online',
                           'BEES' => 'Business Essentials',
                           'O&M' => 'Operations, Mntnc & Control Systems',
                           'HSE' => 'Health, Safety & Environment',
                           'N/A' => 'N/A'};

   public static map<string, decimal> totalLaunchCountMap = new map<string, decimal>();


    global ClientMetrics_Stage6(Map<string, decimal> tlcMap) {

        totalLaunchCountMap = tlcMap;

    }

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

    global void execute(Database.BatchableContext BC, List<sObject> scope) {

.... (I'll stop it here since there's a lot of methods called in the execute portion).

Crop, you're right that I'm getting the error at compile time (ie, when I attempt to save my code) except when using the code I show here, which uses a static declaration of the map and doesn't seem to work anyway. The error simply states that "variable: totalLaunchCountMap doesn't exist".

8
  • why don't you use a constructor for your batch class to pass in variables from whatever is calling the batch class? (that is, don't use the default constructor)
    – cropredy
    Commented Dec 24, 2014 at 18:43
  • crop, thanks for your response, but to be honest I don't understand your suggestion. Can you spell it out for me? (Edit: btw I just renamed the constructor class in my code example to 'BatchClass' to mirror the name of the batch class.. don't know if that's relevant to your suggestion)
    – smohyee
    Commented Dec 24, 2014 at 18:47
  • that is what i was looking for -- but it would be more helpful if you included the declaration for mapVariable and also replace key and value with their types in your real program; then include the exact error message you are getting (compile time error I presume)
    – cropredy
    Commented Dec 24, 2014 at 20:17
  • @Crop, yeah, I abstracted my code so my code example wouldn't be obscured by the details of my program. I'll post an edit with the actual code!
    – smohyee
    Commented Dec 24, 2014 at 20:35
  • @crop1645 OK I've added the actual code from the second batch class (which is meant to take the map variable passed from the first batch class) to my original post under Edit #2. In the first batch class I also declare it as a stateful variable, though I don't give it the global modifier (which, now that I think about it: if the map variable were to be declared as global in the first batch class, couldn't I just reference it in the second batch class without needed to pass it?)
    – smohyee
    Commented Dec 24, 2014 at 20:42

1 Answer 1

4

To pass values from one batch class' s finish() to another's constructor you would have:

First batch class

global class FirstBatchClass implements Database.Batchable<SObject>, Database.Stateful {

 global Map<String,Decimal> someStatefulMap;  // not static
    .... start and execute omitted for clarity

 global void finish(Database.BatchableContext bc) {
   SecondBatchClass sbc = new SecondBatchClass(someStatefulMap);
   Database.executeBatch(sbc);
 }  

}

Second batch class

global class SecondBatchClass implements Database.Batchable<SObject>, Database.Stateful {

 global Map<String,Decimal> someStatefulMap;  // usable by start, execute, and finish


 global SecondBatchClass(Map<String,Decimal> someStatefulMap) {
   this.someStatefulMap = someStatefulMap;  // constructor copies in arg to local instance vbl
 }

 ... start(), execute() and finish() omitted for clarity
}

N.B. The global modifier is no longer required (despite it being in all the apex doc examples) and you can use public instead except for managed packages.

4
  • Awesome thanks for this, I already see a couple lines I didn't have in my attempt. Will try and report back.
    – smohyee
    Commented Dec 30, 2014 at 19:02
  • 3
    OHHH, I think I figured out my problem. My issue wasn't with my attempt to pass the variable into the second batch class (my code is essentially identical to yours)... it was due to the fact that I attempt to access that map variable using a STATIC method in the batch class. And, apparently, static methods are loaded before any object based methods are, so the static method was attempting to access the passed variable before it had actually been passed (via the object instance of the batch class). THANKS so much for guiding me through this!
    – smohyee
    Commented Dec 30, 2014 at 19:57
  • 1
    Sure enough, when I change the static method to an object-oriented one, the 'map variable does not exist' error disappears!
    – smohyee
    Commented Dec 30, 2014 at 19:57
  • cool -- you got me confused with the error being a compile error rather than a runtime error at some point in this whole thread
    – cropredy
    Commented Dec 30, 2014 at 22:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .