4

I would like to insert list of records, catch various exceptions, and if the exception is something I can't handle send the offending objects for manual processing.

I can write most of what I need, but one thing has me stumped, the return information in the SaveResult class after a Database.Insert.

There is a similar question with an accepted answer, however the ID is null so there is not a way to access the object that failed to be inserted. How to capture those records that was unable to get inserted?

From the documentation on SaveResult and Exception handling:

SaveResult

Into To Exception Handlign

SF shows:

Database.insert(contacts, false);

Which returns the SaveResult. However the only methods available in the SR are:

  • getErrors()
  • getId()
  • isSuccess()

None of these help on determining the records that fail on an insert. getErrors provides additional error reporting about the exact error, getId only works on updates, isSuccess is a boolean.

The code below demonstrates my issue:

Account a = new Account(Name = '');
Database.SaveResult SR = Database.insert(a, False);
System.debug('SaveResult: ' + SR);

Debug:

DEBUG|SaveResult: Database.SaveResult[getErrors=(Database.Error[getFields=(Name);getMessage=Required fields are missing: [Name];getStatusCode=REQUIRED_FIELD_MISSING;]);getId=null;isSuccess=false;]

Is there a way to access the failed records on an insert? One thought I had was if there is a failure loop though the original list and weed out the ones that have an ID and handle the exceptions that way. But I think that would get tedious with loop after loops as I try to handle the exceptions and continue to have failures.

UPDATE: As a proof of concept for the above

List<Account> accs = new List<Account>();
Account a = new Account(Name = 'TestingAccOne');
Account b = new Account(Name = '');
accs.add(a);
accs.add(b);
Database.SaveResult[] SR = Database.insert(accs, False);
System.debug('SaveResult: ' + SR);
System.debug('accs: ' + accs);

Debug: SR:

USER_DEBUG [7]|DEBUG|SaveResult: (Database.SaveResult[getErrors=();getId=001R000000sLHNhIAO;isSuccess=true;], Database.SaveResult[getErrors=(Database.Error[getFields=(Name);getMessage=Required fields are missing: [Name];getStatusCode=REQUIRED_FIELD_MISSING;]);getId=null;isSuccess=false;])

List:

USER_DEBUG [8]|DEBUG|accs: (Account:{Name=TestingAccOne, Id=001R000000sLHNhIAO}, Account:{Name=})

From the above I'm leaning toward removing all records from the above list with an ID and then can focus on the offending ones.

Any suggestions and input are greatly appreciated.

2
  • 1
    Yeah mapping partial save results on insert is pretty much impossible. On a try/catch at least you can figure out the DML Index.
    – Adrian Larson
    Commented Apr 23, 2015 at 20:07
  • True, the main reason I went with the Database.Insert was to allow the ones that succeeded to go through. However after looking at the exception class methods as you mentioned I could Try/Catch and determine the dml index, handle those separately and they try another insert at the ones that didn't fail the first time. If they succeeded once, they 'shouldn't' fail the next time. Commented Apr 23, 2015 at 20:32

3 Answers 3

11

There is no actual record to get if the insert fails, and no ID available as the record was never created. What you can do is loop through the resultset, and look at other attributes of your records. The List you are inserting will be in the same order as the resultset. So, you could do something like this:

List<Account> accs = new List<Account>();
Account a = new Account(Name = 'TestingAccOne');
Account b = new Account(Name = '');
accs.add(a);
accs.add(b);
Database.SaveResult[] SR = Database.insert(accs, False);
System.debug('SaveResult: ' + SR);
System.debug('accs: ' + accs);

for(Integer i=0;i<SR.size();i++){
    if(!SR[i].isSuccess()){//only look at failures, or NOT Successes
      system.debug(SR[i]. getErrors()));
      system.debug(accs[i]); //instead of debug, you could have an email processor something else that looks at that record and the error message
    }
}
3
  • 'The List you are inserting will be in the same order as the resultset'. Is there documentation that supports this? I would hate to write something solely based on the order of the list only to find that it the order is the same 90% of the time. Commented Apr 24, 2015 at 19:41
  • Yes, this is stated in the documentation. salesforce.com/us/developer/docs/apexcode/Content/…
    – JimRae
    Commented Apr 24, 2015 at 21:06
  • Must have missed that when I was reading the documentation initially. This is a little cleaner than what I was going to do and from the documentation is SF's recommended way to handle this problem. Thanks @JimRae Commented Apr 27, 2015 at 14:42
1

getId() would return a value only when the DML is a success. There is no way to Id of the object that was never inserted.Even if you use try/catch block to get the Id, the result would be null. getDmlId() method of DMLException class returns Id of the failed record (only when the Id already exist).

-2

There is no way to get ids but you can get failed records like this.

Account[] accts = new List<Account>{
    new Account(Name='Account1'),
    new Account(phone='482937489')};
Database.SaveResult[] srList = Database.insert(accts, false);

List<Account> failedaccounts = new List<Account>();
for (Account a: accts) {
    if(a.Id==null){
        failedaccounts.add(a);
    }
}

system.debug('failedaccounts '+ failedaccounts);
1
  • 3
    This doesn't seem to add much over the accepted answer from 7 years ago.
    – Derek F
    Commented Aug 4, 2022 at 12:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .