9

I need to do one time data update in the org. I plan to do this using batch apex. As this is one time update, I don't want to move batch class to production and don't want to write apex unit tests for it.

I am trying to define batch class in anonymous apex and call it from anonymous apex. Overall structure of anonymous apex is as follows -

 public class batchsample implements Database.Batchable<sObject> {     


     public string query;

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

     public void execute(Database.BatchableContext BC, List<sObject> Scope)
     {
          List<customobj__c> ListToUpdate = new List<customobj__c>();

          for(sobject s: Scope)      
          {
             customobj__c m1 = (customobj__c)s;
             //update some fields here
             ListToUpdate.add(m1);
          }
          if(ListToUpdate.size() > 0)
             update ListToUpdate;
     }

     public void finish(Database.BatchableContext BC)
     {
         //send email      

     }
   } 

batchsample startbatch = new batchsample();
startbatch.query = SOQL query goes here
System.scheduleBatch(startbatch, 'samplebatch', 1, 100);

But when I execute above code as anonymous apex, I get an error

Error - Only top-level classes can implement Database.Batchable

Can someone help me understand why this error is given ? And if there is a way to call batch apex from anonymous apex where Batch Class definition is also part of anonymous apex code ?

20

Basically, when you call Execute Anonymous, it puts your code within a function within an unnamed class. This has the effect of causing anything you write within to be considered in the context of a class.

In fact, if you look at the logs for an Execute Anonymous, you'll see something like this:

CUMULATIVE_PROFILING External entry point: public static void execute(): executed 1 time in 0 ms

So, we can determine that our code is basically placed into a function called execute() in a temporary class.

We can do many things that we can do in a regular class context. You can write static functions:

static void a() { }

But, despite being a "static"-like execution context, you don't need to declare functions as static to access them:

void a() { }

You can't declare static functions inside a class, because inner classes can't have static methods:

class A {
    static void B() { }  // Won't compile
}

Similarly, we can't implement any interfaces that require top-level contexts, including schedulable, queueable, and batchable, because inner classes do not support these interfaces.

As an aside, AsyncApexJob requires an ApexClassId to be scheduled, but your compiled code is never persisted to the database, and therefore does not have an ApexClassId to attach to. Similarly, inner classes don't have their own unique ID, which is why you can't schedule inner classes for later execution.

  • 1
    Nice - I thought this was the case, but couldn't see where it was documented. Thanks for the info. – Caspar Harmer May 17 '15 at 7:18
1

As I understand from the error, I assume that the anonymous apex code is executed in a top-level system apex class. This can be the reason for error that "only top level classes can he schedulable", indicating that this class is not top level class for the runtime.

In my view you have following options

  1. If you have less records ( <20K ), you may be able to execute same anonymous code multiple times (if you can formulate a way to avoid updating already processed records)
  2. Otherwise, you can update the values via Dataloader (suggested approach)
  3. You can also use some ETL tools for this purpose

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.