0

I am receiving the following error.

Constructor not defined: [DynamicBatchApex].(String, String, Map, String) (Line: 9, Column: 30)

If you look at my DynamicBatchApex class, I clearly have the constructor outlined.

Also, what are the positives and downsides of using the global header?

global class NumberOfEmailsSchedulable implements Schedulable
{
      global void execute(SchedulableContext sc) 
      { 
    String query1 = 'SELECT accountId accountId, count( Id) result FROM contact WHERE accountid != null GROUP BY accountId';
    Map<String, Object> field_value_pairs = new Map<String, Object>{'Number_Of_Contacts__c' => 0};
    DynamicBatchApex batch1     =   new DynamicBatchApex('accountId', 'result', field_value_pairs, query1);

     }
}

Batch Apex Class

global class DynamicBatchApex implements Database.Batchable<AggregateResult> {

public String sObjectIdKey;
public String sObjectResultKey;
public Map<String, Object> sObjectFieldsToUpdate;
public String query;


global DynamicBatchApex(String arIdKey, String arResultKey, Map<String, Object> field_value_pairs, String fieldName, String soqlQuery ) 
{


    sObjectIdKey            = arIdKey;
    sObjectResultKey        = arResultKey;

    sObjectFieldsToUpdate   = field_value_pairs;
    query                   = soqlQuery;
}

global Iterable<AggregateResult> start(Database.BatchableContext BC) 
{
    return new BulkIterable(query);
}

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

    DynamicSObjectUpdater sObjectUpdater = new DynamicSObjectUpdater();

    for(sObject sObj: scope)
    {
        AggregateResult ar      = (AggregateResult)sObj;
        ID sObjectId            = (ID)ar.get(sObjectIdKey);
        Decimal arResult        = (Decimal)ar.get(sObjectResultKey);

        sObjectUpdater.getUpdateSObject(sObjectId, sObjectFieldsToUpdate);
    }

    sObjectUpdater.updateSObjects();


}

global void finish(Database.BatchableContext BC) 
{

}
}
3

You have the constructor as below, which takes 5 arguments.

global DynamicBatchApex(String arIdKey, String arResultKey, 
                        Map<String, Object> field_value_pairs, 
                        String fieldName, String soqlQuery ) 

Whereas you are constructing the class as below passing only 4 arguments:

DynamicBatchApex batch1 = 
    new DynamicBatchApex('accountId', 'result', field_value_pairs, query1);

and that there is no such constructor defined which accepts 4 arguments.

Depending on the context, you have two options to correct this:

  • Introduce a 4 args constructor
  • Pass appropriate or null value for other missing argument
4

Whenever you have so many arguments it is better to do the following:

Use a fluent pattern with defaults (my favorite)

public class DynamicBatchApex {
  String arIdKey;
  String arResultKey;
  Map<String,Object> field_value_pairs = new Map<String,Object>();
  String fieldName;  // default to something valid ?
  String soqlQuery;  // default to something valid ?

  public DynamicBatchApex withArIdKey(String val) {this.arIdKey = val; return this;}
  public DynamicBatchApex withArResultKey(String val) {this.arResultKey = val; return this;}
  public DynamicBatchApex withFVPairs(Map<String,Object> val) {this.field_value_Pairs = val; return this;}
  public DynamicBatchApex withFldName(String val) {this.fieldName = val; return this;}
  public DynamicBatchApex withSOQL(String val) {this.soql = val; return this;}

and you invoke as follows in the schedulable class:

public class MySchedulable implements Schedulable {
  public void execute(SchedulableContext sc) {
    Database.executeBatch(new DynamicBatchApex()
     .withArIdKey('someval')
     .withArResultKey('someVal')
     ...
    );
  }
 }

now, where does the schedulable get the values to invoke the batchable? You have several options

  • Pass the values through the schedulable's constructor, using a fluent interface for the schedulable class too
  • Fetch the values from custom metadata (although in this use case, you can probably skip this whole answer and simply call the batchable with the custom metadata key !)

The fluent pattern is self-documenting and easy to extend with more params should you need them

  • Is there more documentation around this design pattern? I am having trouble understanding what is going on here. Would love to dive deeper into this to understand its value... Honestly not sure how I would invoke the DynamicBatchApex class from the schedulable interface with the parameters. – Matthew Metros May 24 at 2:26
  • @MatthewMetros - see Builder Pattern (aka fluent pattern) – cropredy May 24 at 3:02
  • how would I call this from the scheduled interface? Sorry, I am trying to wrap my head around this. I guess I cannot wrap my head around the keyword 'this'. I am a new programmer if this is a noob question. – Matthew Metros May 24 at 12:38
  • 1
    See also: Why Would I Not Enable Chaining? – Adrian Larson May 24 at 15:36
1

You're missing an argument for the fieldName. Either update the your call with the value you need or null. Or, if needed create a new constructor for your object.

To your other question, what are the downsides of using the global access modifier, it means that other applications outside of your application (i.e. web apps). It's generally a bad practice unless it's needed.

From the docs

This means the method or variable can be used by any Apex code that has access to the class, not just the Apex code in the same application. This access modifier should be used for any method that needs to be referenced outside of the application, either in the SOAP API or by other Apex code. If you declare a method or variable as global, you must also declare the class that contains it as global.

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.