The Silver Lining

Lessons & Learnings from a salesforce certified technical architect.

Salesforce Savepoints

with 5 comments

Transaction control is an important part of any system that interacts with a database and Salesforce has neat ways of implementing said control.

Anyone that’s worked with SQL databases will be familiar with savepoints and rolling back, and Salesforce has implemented similar constructs. For those who haven’t heard of these terms wikipedia describes them as

savepoint is a way of implementing subtransactions (also known as nested transactions) within a relational database management system by indicating a point within a transaction that can be “rolled back to” without affecting any work done in the transaction before the savepoint was created.

rollback is an operation which returns the database to some previous state.

As with most things, the importance of these two features is best demonstrated using examples.

You have a single method that inserts two object records which are related by a master-detail relationship. The code

public void myMethod(){

	Parent__c parent;

	try{

		parent = new Parent__c();

		insert parent;

	} catch (System.DmlException e){

		System.debug(e);

	}

	/** Do Some processing **/

	try {

		Child__c child = new Child__c();

		child.parent__c = parent.id;

		insert child;

	}

	catch (System.Dmlexception e){

		System.debug(e);

	}

}

If the DML operation on the Parent object record fails, the Child object record will have been inserted but not attached to any master record. These orphaned records probably get a bit sad because of, you know, the no parent situation. I get a bit sad too as these records float about my database taking up space; using potentially useful primary- and foreign-keys; and increasing query times. In addition to this they can leave VisualForce pages in awkward states.

Luckily savepoints and rollbacks are a piece of cake to implement and solve all these issues in one fell swoop. The revised code

public void myMethod(){

	// Any DML performed after this savepoint will be undone should a rollback occur
	
	Savepoint sp = Database.setSavepoint();
	
	Parent__c parent;
	
	try{
	
			parent = new Parent__c();
			
			insert parent;
	
	} catch (System.DmlException e){
	
	System.debug(e);
	
	}
	
	
	/** Do Some processing **/
	
	
	try {
	
		Child__c child = new Child__c();
		
		child.parent__c = parent.id;
		
		insert child;
	
	}
	
	catch (System.Dmlexception e){
	
		System.debug(e);
		
		// Rollback the database to the state held at the time of defining the savepoint
		
		Database.rollback(sp);
	
	}

}

And Bob’s your uncle. There are a few things to consider when working with savepoints(and rollbacks) that aren’t immediately apparent, viz.

  1. You can continue processing after a rollback but note that only database DML will be undone, variable values will not be rolled back.
  2. You cannot rollback across triggers, as triggers fire in a different context. In slightly more english terms, if you set a savepoint, fire a trigger, and then try to roll back to that savepoint you will get strange results. This one can be troublesome as the error message is a bit lame.
  3. You can only set five savepoints in all contexts. For this reason I find it’s usually a good idea to have a one-to-one ratio between class and test methods.

You can find other considerations in the Force.com Apex Code Developer’s Guide, but these are the ones I commonly encounter.

Advertisements

Written by Wes

June 15, 2009 at 7:28 am

5 Responses

Subscribe to comments with RSS.

  1. Nice post but I would like to add 4th point in things to consider.
    4th point would be view state. when view state changes for a transaction then savepoint becomes null. This is important point to be considered in a big code.

    Harshesh

    January 6, 2011 at 7:01 pm

    • Very important indeed! Thanks for the contribution 🙂

      Wes

      January 6, 2011 at 7:07 pm

  2. Dear Wes,

    Nice Post Indeed . But can we solve a situation like, to retrieve more than 50000 records by SOQL using savepoint . For example:
    * If we are retrieving 50000 record (put SP1 at 25000 and sp2 at 50000) then failure happens at 50001 record.
    * In this case database.rollback(sp2) rolled it back to sp2 state .
    * Now , how can I proceed from here to further retrieve my plus 50000 records in the same context with my same old query(can we do same logic again for my next 50000 records with savepoint 🙂 )

    I know , it looks like little weird to you (if it is completely not possible with sfdc savepoint ) but i need to to know if it is possible .

    Otherwise , We have option like @readonly and batch apex . But ,if above approach works then it will be a very efficient way .

    Thanks and a very Happy Sunday ,
    Mayank Joshi

    Mayank Joshi

    June 17, 2012 at 6:39 am

  3. Technically, your child is actually the parent record and your parent is the child. Children always are the records that point up to/refer to the parent (i.e. Contacts (children) point up to the Account (parent) record via the Contact.AccountId field.)

    Lawrence

    October 14, 2015 at 6:55 pm

    • It’s been 5 years since I wrote this and despite it being viewed more thank 10k times you’re the first person to spot my silly mistake! Thanks 😉

      Wes

      October 14, 2015 at 9:18 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: