4

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

6

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){     

   }
}
|improve this answer|||||
  • 3
    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); – sfdcfox Apr 5 '16 at 21:28
  • so does that mean I can then call my insertAccountsAndContacts() in the execute funtion? @sfdcfox – saint Apr 6 '16 at 7:26
  • @BritishBoyinDC pls see above comment.. thx – saint Apr 6 '16 at 7:28
  • I get this error when i try the solution : "performDML: Class must implement the global interface method: Iterable<SObject> start(Database.BatchableContext) from Database.Batchable<SObject>" – saint Apr 6 '16 at 8:44
  • 1
    @saint Make sure you're changing your batch class's interfaces to match the answer's source. You have to change from using the SObject data type to an Integer data type. – sfdcfox Apr 6 '16 at 16:43
0

try like this..it will work

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

PRIVATE STATIC FINAL BOOLEAN LOG_THIS_CLASS = TRUE;
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 = 1500;
Integer contactLoopSize = 3000;
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;
 }


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){

    }
|improve this answer|||||
  • Hi Lalit, welcome to SFSE. Please take a moment to scroll through the tour and read How to Answer. Right now, your post contains only code with no explanation. We tend to call this a code dump, and these answers are not as useful to the community nor the question asker. Please take a moment to edit your post to add some explanation. – Adrian Larson Apr 11 '19 at 11:15

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.