[SalesForce] How to create thousands of records using batch apex

I have a scenario where I have to create 30000 contacts and 15,000 accounts (i.e. 2 contacts per account) in an org. I have been trying to do this by using batch apex but I still have error of "First error: Too many DML rows: 10001". Is there any way of overcoming this..? I have done a little research into using Iterator with batch class but not quite sure how this works. Here is what I have attempted so far:

global class performDML implements Database.Batchable<sObject>, Database.Stateful {

PRIVATE STATIC FINAL BOOLEAN LOG_THIS_CLASS = TRUE;

global Database.QueryLocator start(Database.BatchableContext BC){
    if (LOG_THIS_CLASS) System.debug('in start');
    insertAccountsAndContacts();
    String query = 'SELECT id, isDeleted FROM Account WHERE  isDeleted = false';
    if(test.isRunningTest()){
        query += ' LIMIT 10';
    }
    return Database.getQueryLocator(query); //Dummy query
}

global void execute(Database.BatchableContext BC, list<SObject> sc){
    if (LOG_THIS_CLASS) System.debug('in execute');
    performUpdate();
}

global void finish(Database.BatchableContext BC){

}

global static void insertAccountsAndContacts(){
    if (LOG_THIS_CLASS) System.debug('in insertAccountsAndContacts()');
    String accountName = 'Dummy Account ';
    String contactName = 'Dummy Contact ';
    List<Account> accountList = new List<Account>();
    List<Contact> contactList = new List<Contact>();
    Account acc;
    Contact con;
    Integer accountLoopSize = 15000;
    Integer contactLoopSize = 30000;
    for (Integer i = 0; i < accountLoopSize; i++){
        acc = new Account(Name = accountName + i);
        accountList.add(acc);
    }
    for (Integer j = 0; j < contactLoopSize; j++){
        con = new Contact(LastName = contactName + j);
        contactList.add(con);
    }
    insert accountList;
    insert contactList;
}

global static void performUpdate(){
    if (LOG_THIS_CLASS) System.debug('in performUpdate()');
    List<Contact> contactList = [SELECT Id, LastName FROM Contact WHERE LastName LIKE 'Dummy%'];
    List<Account> accountList = [SELECT Id, Name FROM Account WHERE Name LIKE 'Dummy%'];
    List<Contact> contactListToUpdate = new List<Contact>();
    Integer count;
    for(Contact cons : contactList){
        count = 0;
        for(Account accs : accountList){
            cons.AccountId = accs.Id;
            contactListToUpdate.add(cons);
            count ++;
            if (count == 2) break;
        }
    }
    update contactListToUpdate;
}

}

and I call this by:

performDML b = new performDML();
Database.executeBatch(b, 200);

Any help is much appreciated.

Thanks

Best Answer

So as written, that doesn't really operate as a batch should - the batch just calls the one method and tries to create all the records in one execution...a batch is designed to run as a series of iterations. I think what you need is a batch that uses a custom iterator - so take a look at this StackEx post. In that example, they use a String Iterator, but I think you need to use an Integer Iterator, or maybe main state and keep count.

Essentially, you need the batch to only create ~200 accounts per execution to stay within the limits

Here's a better formatted version of SFDCFox's example:

Basic iterator:

public class IntegerIterator implements Iterable<Integer>, Iterator<Integer> { 
Integer counter; 

public IntegerIterator(Integer counter) {
 this.counter = counter; 
} 

public Boolean hasNext() { return counter > 0; } 

public Integer next() { return counter--; } 

public Iterator<Integer> iterator() { return this; } 
} 

In the start method, just return a value to count to: return new IntegerIterator(1000);

*****Edit***** And then the batch would look something like this:

public class AccountBatchUpdate implements Database.batchable <Integer> { 

   public Iterable<Integer> start(Database.batchableContext info){ 
       return new IntegerIterator(1000);   
   } 

   public void execute(Database.batchableContext info, List<Integer> scope){ 
   //Loop through integer list and create records
   }

   public void finish(Database.batchableContext info){     

   }
}
Related Topic