In my haste to get a new batch class running I accidentally packaged it such that the start() method returns an iterable (List<SObject>) instead of a Query Locator. Bad developer, no Twinkie.

Annoyingly the interface is global so that it can be invoked via anonymous apex inside of client orgs, and yet a client's org has too many rows in the queried object to run (> 50k). It seems that I don't have an option to overload this: even trying to use @deprecated and adding a second start() method that leverages a Query Locator does not work.

I've contacted support to see if I can get the class removed from the package since this class is only in a single org anyway, but can anyone think of a good way around the problem short of adding a second class?

  • How about adding a virtual keyword so that you can overload ?I have not tried to batch start method but guessing at this point . – Mohith Shrivastava Oct 13 '16 at 6:21
  • 1
    No go... "method already defined". – Matt Lacey Oct 13 '16 at 6:28
  • Is it an option to deprecate the current class and create a new one? Any approach will be painful at this point. – Lance Shi Oct 13 '16 at 7:00
  • @LanceShi That's all I can think of, really want to keep it clean. – Matt Lacey Oct 13 '16 at 11:08

If you can convince the "one org" to uninstall the package, Salesforce can roll back the package to a "beta" version, which will let you fix your mistake. Your client will be inconvenienced, though, because you'll have to export all the data, uninstall, reinstall, and reload all the data. It's not good. Otherwise, you'll have to go with a new class entirely. For the record, I'd personally recommend not using either of the documented return types in the future, and use Iterable<Object> instead. Iterable<Object> is implemented by List<SObject> (and all the specific types) as well as Database.QueryLocator. This gives you the flexibility to choose which you'd like to use later.

For some reason that's beyond me, Database.QueryLocator won't return as an Iterable<Object> unless you cast it, making the basic framework look like this:

global class BatchProcess implements 
    Database.Batchable<Object>, Database.AllowsCallouts, Database.Stateful {

    String query;

    global Iterable<Object> start(Database.BatchableContext context) {
        return (Iterable<Object>)Database.getQueryLocator(query);
    global void execute(Database.BatchableContext context, Object[] scope) {
    global void finish(Database.BatchableContext context) {
  • So you still get the 50 million row ability without locking in QueryLocator as the return type? Nice. – Keith C Oct 13 '16 at 7:40
  • That's a pretty sneaky way of doing it! Definitely going to use that in the future. – Matt Lacey Oct 13 '16 at 11:09
  • 1
    @KeithC Yes, I found this technique in a quest to make a flexible exec anon batch class. You still get all your normal limits, but you can return lists of normal objects, a query locator, and even custom Iterable objects. – sfdcfox Oct 13 '16 at 13:13
  • Can't remove the package and haven't found any way of tricking the checker through casts and the like so it looks as though I'm stuck with it. This is what happens when you have to package to try out new ideas in a client org :/ – Matt Lacey Oct 13 '16 at 22:46
  • My colleague @StanK can't get a batchable where Iterable<Object> is the return type of start to compile in the current and earlier API versions and neither can I. What are we missing? – Keith C Oct 18 '16 at 10:45

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.