+ Start a Discussion
Dan Blackhall.ax1171Dan Blackhall.ax1171 

Id's exist on objects after Database rollback

I've created this test controller to illustrate my problem

 

public class TestController {
	List<Object_A__c> Aobjects { get; set; }
	List<Object_B__c> Bobjects { get; set; }

	public TestController() {
		//assign values to the a and b lists
	}

	public PageReference pageAction() {
		Savepoint sp = Database.setSavepoint();
		try {
			//Assume this dml operation succeeds
			insert Aobjects;
			//assume this dml operation fails
			insert Bobjects;
		} catch(System.DmlException e) {
			Database.rollback(sp);
			ApexPages.addMessages(e);
		}
	}
}

 

When the pageAction is called and there is a rollback because of a problem with 'B objects', the list of 'A Objects' still have Id values.

 

This becomes a problem when the pageAction is called a second time, because the 'A objects' have Ids and an insert is performed, giving the error: Record ID: cannot specify Id in an insert call

 

What is the best way to solve this?

Best Answer chosen by Admin (Salesforce Developers) 
Dan Blackhall.ax1171Dan Blackhall.ax1171

No worries, thanks for the quick replies.

The reason there is a rollback is that in my real code this is a wizard that creates the lists of objects, but if there is a database failure none of the objects should remain in the database.

 

I was able to solve the problem by doing some cleanup in the catch block:

 

		} catch(System.DmlException e) {
			Database.rollback(sp);
			ApexPages.addMessages(e);
			//Cleanup the 'A Objects' by cloning them so they no longer have an Id value
			this.Aobjects = this.Aobjects.clone();
		}

 

 

 

For anyone else reading this, make sure your code is version 23 for this to work because according to the apex docs preserve Id defaults to true for versions <= 22

All Answers

Starz26Starz26

use upsert instead of insert on the a object...

 

The ID's for a exist because the first time the code ran it was successful. The second time the savepoint and database state are with the new a records so when b fails on the second attempt it rolls back to the start of the second attempt...

 

 

Dan Blackhall.ax1171Dan Blackhall.ax1171

That doesn't work, Salesforce tries to update the 'A object' because it has an Id, but the Id does not represent an object in the database because it was rolled back.

The exact error message is:

System.DmlException: Upsert failed. First exception on row 0 with id a0AF0000006pYVYMA2; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id
Starz26Starz26

Yea, sorry about that, you cannot upsert and set an ID for new records so it will fail on the first run unless you are using external ID's...

 

are B records being inserted correctly the first time? It sounds like they are on the first run as you do not roll back at all since the a records exiost after the first run....

 

Maybe posting a bit more of your code.....

Dan Blackhall.ax1171Dan Blackhall.ax1171

No worries, thanks for the quick replies.

The reason there is a rollback is that in my real code this is a wizard that creates the lists of objects, but if there is a database failure none of the objects should remain in the database.

 

I was able to solve the problem by doing some cleanup in the catch block:

 

		} catch(System.DmlException e) {
			Database.rollback(sp);
			ApexPages.addMessages(e);
			//Cleanup the 'A Objects' by cloning them so they no longer have an Id value
			this.Aobjects = this.Aobjects.clone();
		}

 

 

 

For anyone else reading this, make sure your code is version 23 for this to work because according to the apex docs preserve Id defaults to true for versions <= 22

This was selected as the best answer
Keith987Keith987

The clone call on a list is a shallow copy; its the deepClone call that copies the SObjects and drops the ids.