3

I've got a function that does DML on a couple of different objects, one after the other. Looks something like this:

Savepoint sp = Database.setSavepoint();
try {
    Account[] accts = someList;
    Contact[] conts = someOtherList;
    Opportunity[] opps = yetAnotherList;

    Database.insert(accts);
    Database.insert(conts);
    Database.update(opps);
} catch(DmlException e){
    Database.rollback(sp);
    //log an error based on the dml message and original sObject type
} catch(Exception e){
    Database.rollback(sp);
    //log a general error
}

As you can see, in the first catch block, I want to log an error based on the type of the object that threw the exception. Is there any way, other than getting the line number from which the exception was thrown, to get the sObject type from the DmlException?

2

The way I solved it in the end is like this:

Savepoint sp = Database.setSavepoint();
Boolean errors = false;
Account[] accts = someList;
Contact[] conts = someOtherList;
Opportunity[] opps = yetAnotherList;

Database.SaveResult acctsRes = Database.insert(accts, false);
for(Integer i=0; i<acctsRes.size(); i++){
    if(!acctsRes[i].isSuccess()){
        errors = true;
        //handle errors
    }
}

Database.SaveResult contsRes = Database.insert(conts, false);
for(Integer i=0; i<contsRes.size(); i++){
    if(!contsRes[i].isSuccess()){
        errors = true;
        //handle errors
    }
}

Database.SaveResult oppsRes = Database.update(opps, false);
for(Integer i=0; i<oppsRes.size(); i++){
    if(!oppsRes[i].isSuccess()){
        errors = true;
        //handle errors
    }
}

if(errors){
    Database.rollback(sp);
}
1

How about something as simple as setting object name before each insert?

Savepoint sp = Database.setSavepoint();
String err;
try {
    Account[] accts = someList;
    Contact[] conts = someOtherList;
    Opportunity[] opps = yetAnotherList;

    err = 'inserting accts';
    Database.insert(accts); 

    err = 'inserting conts';
    Database.insert(conts);

    err = 'inserting opps';
    Database.update(opps);
} catch(DmlException e){
    Database.rollback(sp);
    System.Debug(err);
} catch(Exception e){
    Database.rollback(sp);
    //log a general error
}
  • This could work, but it feels a bit hacky – rael_kid Jul 16 '15 at 9:32
  • That's true. Personally, I would create some wrapper method over the Database.insert() that would return the error message if there is any. Also the object name could be retrieved in more generic manner, from describe. This way it would be much more elegant. – Bart Juriewicz Jul 16 '15 at 9:38
1

Is you closely observe the Exception methods here especially for DMLException, you will see that there is no method that would return the name of Object.

Now in your case, one option could have been using the method - getDmlId(). This will return ID of the failed record. But since your code has 2 Insert DMLs i.e. account and Contact, its impossible to differentiate between them using this method as getDMLid() will always be NULL.

Next method you can explore is getDmlFieldNames() which will return the list of field, so in this case you will have to apply logic to detect on which object the fields with error are present. So again this is not much maintainable. So I would suggest applying atleast 2 try catch block on for one. Split Account/Opportunity and Contact and also introduce a boolean (if required) that will ensure contact DML will happen only if Account/Oppty is Successful like below -

   boolean insertContactFlag = false;
Savepoint sp = Database.setSavepoint();

try {
    insertContactFlag = true;
    Account[] accts = someList;
    Opportunity[] opps = yetAnotherList;
    Database.insert(accts);
    Database.update(opps);
} catch(Exception e){
    insertContactFlag = false;
    if(e.getTypeName() == 'DmlException'){
        //if getDMLId() is not Null,its Opportunity ELSE its Account
        //log an error based on the dml message
    } else {
        //log a general error
    }
    Database.rollback(sp);
}
if(insertContactFlag){
    try {
        Contact[] conts = someOtherList;
        Database.insert(conts);
    } catch(Exception e){
        insertContactFlag = false;
        if(e.getTypeName() == 'DmlException'){
            //log an error based on the dml message [Contact]
        } else {
            //log a general error
        }
        Database.rollback(sp);
    }
}
  • I think this could work. I think maybe even a single try catch block for each DML operation would be better. Considering I might add more DML statements to the function later on (maybe even deletes/upserts). – rael_kid Jul 16 '15 at 8:52
  • Actually, thinking of it like that; I might as well use false for all the allOrNone parameters for the database functions and just handle the SaveResults right after the DML – rael_kid Jul 16 '15 at 8:55
  • Yes, you can surely use allorNone too, but please be assured, if you apply try catch around "database.insert(accList,False)" the code will never go in catch block. – Amit Bangad Jul 16 '15 at 9:00

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.