3

I am facing an error when I am using savepoint and rollback while saving a record:

Here is my code

public PageReference saveRecords(){
    //Boolean to signify if all DML operations during save are successful 
    Boolean isSaveError = false;
    // Create a savepoint while no saving is done
    Savepoint sp = Database.setSavepoint();
    //save record
    try{
        upsert objetcA;
    }catch(Exception dmlEx){
        isSaveError = true;
        Database.rollback(sp);
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,dmlEx.getDmlMessage(0))); 
    }

    //now do a DML on ObjectB
    try{
        upsert objetcB;
    }catch(Exception dmlEx){
        isSaveError = true;
        Database.rollback(sp);
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,dmlEx.getDmlMessage(0))); 
    }

    if(!isSaveError){
        //redirect somewhere
    }
}

Now the issue scenario is:

  1. When save method is called objectA saves successfullt but there is an exception in upserting objectB
  2. User corrects the data and clicks on save button again to call saveRecords method.
  3. saveRecords method is called again and but an error is thrown : 'Invalid cross reference key'.

This is happening probably because when saveRecords method is called first time an id is assigned but due to error in objectB the transaction is rolled back. but and Id is assigned to objectA. When calling second time the system sees an Id associated with record and tries to update it but cannot find it in database hence error is thrown.

Any elegant way of resolving this?

3

When objectA is inserted it is given an ID and that ID resides on the object in code and in the DB. When objectB is inserted and the error happens the database is rolled back. So as far as it is concerned objectA no longer exists (and its ID no longer exists either). However it is still on the object in your code, so when you try to upsert it, it both cannot update a record with an ID that it says doesn't exist, nor will it insert a record with an ID. My suggestion is to clear the Id of objectA after the rollback.

try{
    upsert objetcB;
}catch(Exception dmlEx){
    isSaveError = true;
    Database.rollback(sp);
    objectA.Id = null;
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,dmlEx.getDmlMessage(0))); 
}
2
  • Hi Doug, thanks for replying. This solution may work in insert scenario but may work in update scenario, where the record id is already present. Maybe i should use a boolean to identify edit mode and use that?
    – Shahid Ali
    May 11 '16 at 18:49
  • @ShahidAli Perhaps that would be best. I don't know the specifics of your situation, would it be worth it to separate out the creation and update entirely?
    – dBeltowski
    May 11 '16 at 18:56
0

in addition to what Doug suggested this is what I ended up using as workaround and got it working.

try{
    upsert objetcB;
}catch(Exception dmlEx){
    isSaveError = true;

    if(!isEditMode){  
        oDonation.Id = null;
    }

    Database.rollback(sp);
    objectA.Id = null;
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,dmlEx.getDmlMessage(0))); 
}

I introduced a Boolean isEditMode which identified if it is a new insertion. In case of new insertions clear the id field to avoid the error. Id should be cleared in new insertion mode only not in case of update else it will create another record.

1
  • It seems like his answer essentially resolved the issue for you then. You should accept it as correct.
    – Adrian Larson
    May 11 '16 at 19:22
0

You just need to use a clone before

    objType tmp = objetcB.clone(true);
try{
    upsert objetcB;
}catch(Exception dmlEx){
    Database.rollback(sp);
    objetcB = tmp;
}

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.