Make a class to batch an existing class?

It's certainly possible, but I wouldn't qualify it as easy. The way I would do it is to write a SandboxPostCopy script that enqueued a batch class.

This seems like a good spot to use the relatively rare batch class that iterates over a primitive or enumerated type, rather than an sObject query. The execute() method can then look at the current entity with a switch on statement and decide which batch of methods to call. I think the skeleton would look like this, and I'm cribbing some of the structure from the Nonprofit Success Pack's telemetry batches:

// define an enum somewhere
public enum PostCopyStep {
    JediStudents,
    StarWarsPrograms,
    CampaignHierarchy,
    // and so on
}

Then, the batch class looks kind of like this, allowing it to iterate over a list of post copy steps in units of one and execute each in a separate transaction.

public without sharing class PostcopySandboxPopulationbBatch implements Database.Batchable<PostCopyStep> {

    private static final List<PostCopyStep> postCopySteps = new List<PostCopyStep> {
        JediStudents,
        StarWarsPrograms,
        CampaignHierarchy,
        // etc.
    };

    public Iterable<PostCopyStep> start(Database.BatchableContext bc) {
        if (!Test.isRunningTest()) {
            return postCopySteps;
        } else {
            return new List<PostCopyStep>{JediStudents}; // can't run multiple batches in a test.
        }
    }

    public void execute(Database.BatchableContext bc, List<PostCopyStep> step) {
        switch on (step[0]) {
            when JediStudents {
                StarWarsTestDataUtil.createJediStudents();
                StarWarsTestDataUtil.createStarWarsCompaniesWithMentors();
                StarWarsTestDataUtil.createStarWarsRecruitCamp();
            }
            when StarWarsPrograms {
                StarWarsTestDataUtil.createStarWarsPrograms();
                StarWarsTestDataUtil.putMentorsOnCampaign();
                StarWarsTestDataUtil.putStudentsOnCampaign();
                StarWarsTestDataUtil.putProgramOnCatEnts();
                StarWarsTestDataUtil.putMentorships();
                StarWarsTestDataUtil.createRegionSchools();
            }
            // etc.
        }
    }

    public void finish(Database.BatchableContext bc) {
    }
}

Your post copy class would run the batch using a batch size of 1.

The additional trick is that you'll have to write test classes to validate the batch class before you can deploy it to production, and because there are so many logical branches involved it may be rather tedious. This is a place where I'd generally just call the execute() method directly in a test class (supplying each enum value in sequence) rather than trying to actually enqueue the batch.


Take the basic structure of a batch class: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm

Start, execute, finish.

Then chain the batches together. So batch 1 would execute be two or 3 methods and then finish would point to batch 2, which would execute 2 or 3 methods depending on how big they are. And so on and so on till you've got them all.


You can use the chaining mechanism i.e. you can call your same batch class from finish method but with different parameters which you can pass via constructor of batch class. In this way, you can mention which methods needs to be called in that specific run.

For this, you can have your entire code of data factory in execute method of batch class, just that you will have to segregate your methods so that it will execute only few methods at a time i.e. whichever is passed as parameter.

Sample batch class would look like this

global class CreateSampleData implements Database.Batchable<sObject>, Database.Stateful {
    String runChunk;
    global CreateSampleData(String strRunFor) {
        runChunk = strRunFor;
    }

    global Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator([SELECT Id, Name FROM Organization]);
    }

    global void execute(Database.BatchableContext BC, List<sObject> scope) {
        if(runChunk=='First Chunk'){
            StarWarsTestDataUtil.createJediStudents();
            StarWarsTestDataUtil.createStarWarsCompaniesWithMentors();
            StarWarsTestDataUtil.createStarWarsRecruitCamp();
        } else if (runChunk=='Second Chunk'){
            StarWarsTestDataUtil.createStarWarsPrograms();
            StarWarsTestDataUtil.putMentorsOnCampaign();
            StarWarsTestDataUtil.putStudentsOnCampaign();
            StarWarsTestDataUtil.putProgramOnCatEnts();
            StarWarsTestDataUtil.putMentorships();
            StarWarsTestDataUtil.createRegionSchools();
        } else if (runChunk=='Third Chunk'){
            StarWarsTestDataUtil.createCampaignHierarchy();
            StarWarsTestDataUtil.individualsInsert();
            StarWarsTestDataUtil.majorDonationInsert();
            StarWarsTestDataUtil.mediumDonationInsert();
            StarWarsTestDataUtil.smallDonationInsert();
        } else if(runChunk=='Fourth Chunk'){
            StarWarsTestDataUtil.regionaldonoraccounts();
            StarWarsTestDataUtil.corporateFeesInsert();
            StarWarsTestDataUtil.grantsInsert();
        }
    }

    global void finish(Database.BatchableContext BC) {
        if(runChunk=='First Chunk'){
            runChunk = 'Second Chunk';
            Database.executeBatch(new CreateSampleData(runChunk));
        } else if(runChunk=='Second Chunk'){
            runChunk = 'Third Chunk';
            Database.executeBatch(new CreateSampleData(runChunk));
        }  else if(runChunk=='Third Chunk'){
            runChunk = 'Fourth Chunk';
            Database.executeBatch(new CreateSampleData(runChunk));
        }
    }
}

You can call this batch class from anonymous window with single line

Database.executeBatch(new CreateSampleData('First Chunk'));

Note : You can have meaningful names for all the chunks, you will have to make that respective changes in the code as well. Also, you can segregate the chunks to have data factory methods according to the volume of data each method is creating.

Also, make sure that batch does not go into recursion. All your conditions should be properly written in finish method, and don't add an else clause in finish method.

Tags:

Apex

Batch