function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
David SilvaDavid Silva 

How to create generate generic batch apex

Hi

How to generate a batch class that is generic that
 can accept all the sObjects and some criteria?
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for batch job
1) http://amitsalesforce.blogspot.in/2016/02/batch-apex-in-salesforce-test-class-for.html


Batch Apex
A Batch class allows you to define a single job that can be broken up into manageable chunks that will be processed separately.

When to use Batch Apex
One example is if you need to make a field update to every Account in your organization. If you have 10,001 Account records in your org, this is impossible without some way of breaking it up. So in the start() method, you define the query you're going to use in this batch context: 'select Id from Account'. Then the execute() method runs, but only receives a relatively short list of records (default 200). Within the execute(), everything runs in its own transactional context, which means almost all of the governor limits only apply to that block. Thus each time execute() is run, you are allowed 150 queries and 50,000 DML rows and so on. When that execute() is complete, a new one is instantiated with the next group of 200 Accounts, with a brand new set of governor limits. Finally the finish() method wraps up any loose ends as necessary, like sending a status email.

Sample Batch Apex
1) Start
method is automatically called at the beginning of the apex job. This method will collect record or objects on which the operation should be performed. These record are divided into subtasks & passes those to execute method.

2) Execute Method performs operation which we want to perform on the records fetched from start method.

3) Finish method executes after all batches are processed. Use this method to send confirmation email notifications.

Let us know if this will help you

Thanks
Amit Chaudhary
 
David SilvaDavid Silva
Hi Amit,
Above example the batch class is acting on accout object.
I need to know how to create a generic batch class that can accept any object.

With Regards,
   David.
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for Sobject
1) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm
you can try below code for sObject
global class SearchAndReplace implements Database.Batchable<sObject>{

   global final String Query;
   global final String Entity;
   global final String Field;
   global final String Value;

   global SearchAndReplace(String q, String e, String f, String v){

      Query=q; Entity=e; Field=f;Value=v;
   }

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

   global void execute(Database.BatchableContext BC, List<sObject> scope){
     for(sobject s : scope){
     s.put(Field,Value); 
     }
     update scope;
    }

   global void finish(Database.BatchableContext BC){
   }
}
Let us know if this will help you

Thanks
Amit Chaudhary
 
Amit Chaudhary 8Amit Chaudhary 8
Please try below batch job.
global class DeleteJob implements Database.Batchable<sObject>{

   global final String Query;

   global DeleteJob(String q)
   {
      Query=q; 
   }

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

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

   global void finish(Database.BatchableContext BC){
   }
}
If you want to delete account recrod then execute above batch job like below
 
DeleteJob  obj = new DeleteJob('select id from account');
            DataBase.executeBatch(obj);
If you want to delete contact record then like below
DeleteJob  obj = new DeleteJob('select id from Contact');
            DataBase.executeBatch(obj);
Let us know if this will help you. You just need to pass dynamic query.

Thanks
Amit Chaudhary
 
Amol_BagdeAmol_Bagde
Here is a working code you can use as generic apex batch. It can be optimized more to get the field data type from metadata.

Class File:-
public class GenericBatch implements Database.Batchable<sObject>, Database.Stateful {
    private String whereClause;
    private String objectAPIName;
    private Map<String, String> fieldsDefinition;
    private Map<String, String> fieldsValues;
    public GenericBatch nextGenericBatch;
    public Integer nextBatchJobCount;

    public GenericBatch(String objectAPIName, String whereClause,
                       Map<String, String> fieldsDefinition, Map<String, String> fieldsValues){
        this.objectAPIName = objectAPIName;
        this.whereClause = whereClause;
        this.fieldsDefinition = fieldsDefinition;
        this.fieldsValues = fieldsValues;
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {
        String query = 'SELECT Id';
        for(String fieldName : fieldsValues.keySet()){
            query += ', ' + fieldName;
        }
        query += ' FROM ' + objectAPIName;
        if(!String.isEmpty(whereClause)){
            query += ' WHERE ' + whereClause;
        }
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext bc, List<sObject> objectList){
        for(sObject obj : objectList){
            for(String fieldName : fieldsValues.keySet()){
                String fieldValue = fieldsValues.get(fieldName);
                String fieldType = fieldsDefinition.get(fieldName);
                if(fieldType == 'Integer'){
                    obj.put(fieldName, Integer.valueOf(fieldValue)) ;
                }else if (fieldType == 'Date'){
                    obj.put(fieldName, Date.valueOf(fieldValue)) ;
                }else if (fieldType == 'Boolean'){
                    obj.put(fieldName, Boolean.valueOf(fieldValue)) ;
                }else if (fieldType == 'Datetime'){
                    obj.put(fieldName, Datetime.valueOf(fieldValue)) ;
                }else if (fieldType == 'Decimal'){
                    obj.put(fieldName, Decimal.valueOf(fieldValue)) ;
                }else if (fieldType == 'Double'){
                    obj.put(fieldName, Double.valueOf(fieldValue)) ;
                }else if (fieldType == 'Long'){
                    obj.put(fieldName, Long.valueOf(fieldValue)) ;
                }else {
                    obj.put(fieldName, fieldValue) ;
                }
            }
        }
        update objectList;
    }

    public void finish(Database.BatchableContext bc){
        if(nextGenericBatch != null){
            if(nextBatchJobCount == null){
                nextBatchJobCount = 200;
            }
            Database.executeBatch(nextGenericBatch, nextBatchJobCount);
        }
    }
}

Below code will tell you how to call it.
 
//Field which needs to be updated with its Data type
Map<String, String> caseFields = new Map<String, String>();
caseFields.put('ContactEmail', 'String');
caseFields.put('Status', 'String');

// Fields Values
Map<String, String> caseFieldValues = new Map<String, String>();
caseFieldValues.put('ContactEmail', 'test@test.com');
caseFieldValues.put('Status', 'Open');

String whereClause = ' Status != \'New\'';
GenericBatch caseBatch = new GenericBatch('Case', whereClause, caseFields, caseFieldValues);

Database.executeBatch(caseBatch);

Also you can create another instance of GenericBatch and assign it to the vairable "nextGenericBatch". So once first batch is competed, another batch will start. It can be of same object or different.
Charles ThompsonCharles Thompson
+1 @Amol_Bagde
Excellent solution.  Thanks for posting.