Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

How can we avoid trigger get executed again and again (recursive trigger)? One way is to have a class with static variable and have boolean value and check it is true or false and changing the boolean according to executing the trigger value. Is there any other way other than this to handle recursive trigger in salesforce. Please guide me the best way to do ?

share|improve this question
1  
If you google search your question, you should find other questions, with different ways of handling this situation. Here is one as an example: salesforce.stackexchange.com/questions/14162/recursive-trigger –  Jenny B Aug 13 at 15:04

2 Answers 2

Another good way to avoid recursion is highlighted in Chapter 6 of Dan Applemans's excellent book, Advanced Apex Programming. This particular example is around recursion when looking for a specific field change which is very common in many triggers.

As mentioned in @BarCotters answer, you generally run your trigger on very specific criteria. A change in a field can trigger certain logic in your trigger. If you look at the Order of Operations for triggers, you can see that any workflow rules and field updates based on those rules are run after the both before and after triggers have run. It runs however prior to the changes actually being committed to the DB, which is where this can cause problems when detecting field changes.

So lets say we are using a very simple example where we have a trigger on the Opportunity object that looks for opportunities that have been Won, and based on that it creates a new custom object record.

trigger OpportunityTrigger on Opportunity (after update) {
    map<Id,Opportunity> justWonOppsMap = new map<Id,Opportunity>();
    for (Opportunity o : Trigger.new) {
        if (o.isWon != Trigger.oldMap.get(o.Id).isWon) {
            justWonOppsMap.put(o.Id, o);
        }
    }
    List<Some_Custom_Object__c> objs = new list<Some_Custom_Object__c>();
    for(Opportunity o : justWonOppsMap.values()){
         objs.add(new Some_Custom_Object__c(
              Name = 'New object based off ' + o.Name, 
              Opportunity__c = o.Id
         ));
    }
    insert objs;
}

This trigger will work fine, and will create a new custom object when you close an Opportunity. It will work fine that is, assuming you have no WFR's that have field updates on the Opportunity Object.

Let's say now I had a workflow rule on the Opportunity that When an Opp is closed, it changes the CloseDate of the Opportunity to Today. (A fairly simple and common WFR that many admins may add).

That simple change now Breaks my trigger. This same trigger will now actually create 2 custom objects when my opportunity is closed. This is due to the fact that the WFR rule is now firing the triggers one final time.

This is how you would think the logic works

  • Trigger Run 1 (Old Value = Not Won, New Value = Won)
  • Workflow Trigger
  • Run 2 (Old Value = Won, New Value = Won)

This is not how it works though, even though the opp has been updated, it has not yet been committed to the Database, so in the second run of the trigger it still see's the opp and just being closed like below

This is actually how it works

  • Trigger Run 1 (Old Value = Not Won, New Value = Won)
  • Workflow
  • Trigger Run 2 (Old Value = Not Won, New Value = Won) Identical as first run

This is where Dan Appleman's solution comes in. He advises to use a mechanism that actually checks for the 'correct old value'. This would allow the second run of the trigger to detect the value that was set in the first run of the trigger.

trigger OpportunityTrigger on Opportunity (after update) {
    OpportunityTriggerHelper.OppAfterUpdate(trigger.ew, trigger.old, trigger.newMap, trigger.oldMap);
}

public class OpportunityTriggerHelper {

    Private static Map <Id,boolean> oldIsWonMap = null;

    public static void OppAfterUpdate(list<Opportunity> newOpps, list<Opportunity> oldOpps, map<Id,Opportunity> newMap, map<Id,Opportunity> oldMap) {

        if(oldIsWonMap == null) {
            oldIsWonMap = new map<Id,boolean >();
        }

        map<Id,Opportunity> justWonOppsMap = new map<Id,Opportunity>();
        for (Opportunity o : Trigger.new) {

            //This checks to see if there was a value set in a previous trigger run
            boolean oldIsWon = (oldIsWonMap.containsKey(o.id)) ? oldIsWonMap.get(o.id) : oldmap.get(o.id).isWon;

            //this checks the current opp value with the 'correct' old value
            if(o.isWon && !oldIsWon){
                justWonOppsMap.put(o.Id, o);
            }

            //this puts in the 'correct' old value in case the trigger is run again in the same context
            if(oldIsWon != o.isWon) {
                oldIsWonMap.put(o.id,o.isWon);
            }
        }
        List<Some_Custom_Object__c> objs = new list<Some_Custom_Object__c>();
        for(Opportunity o : justWonOppsMap.values()){
            objs.add(new Some_Custom_Object__c(
                Name = 'New object based off ' + o.Name, 
                Opportunity__c = o.Id
            ));
        }
        insert objs;
    }
}

With this change, now the WFR rule does not break our trigger. Only 1 custom object is created when the opp is closed.

I know this was longwinded, but I hope it helps. I would highly getting this book as this was only a few pages worth of goodness, and this book is jam packed with great knowledge that I think all SFDC developers should have.

Heres the link again.

http://advancedapex.com/

share|improve this answer
    
very nice @sfdc_ninja - however, note OP which asks: "how to avoid without static variables" - your solution, which is of course, very good - using fine grained static variables rather than coarse trigger-level static variables, still uses static variables. –  crop1645 Aug 13 at 16:17
    
I actually edited the question to say that, that was not the original wording of the question. But as you are right, I will change it back. –  sfdc_ninja Aug 13 at 16:21
    
np, I was a bit wary of critiquing a 'ninja' –  crop1645 Aug 13 at 16:26
    
lol...No worries. I tried to edit the question because the way its worded now, it just looks like a repeat of a question asked many times, but this one is a bit different. Knows of a simple technique, just looking for different approaches and solutions. Can't think of a good rewording of the question. –  sfdc_ninja Aug 13 at 16:27
1  
i reworded question -- what do you think? –  crop1645 Aug 13 at 16:33

Your trigger should only be firing on very specific criteria. Make them as specific as possible by comparing old and new field values to make sure your criteria is met.

For example if you wanted to only fire when a contacts BirthDate changed then you could do the following:

trigger ContactBeforeTrigger on Contact (before insert, before update, before delete) {
    if (Trigger.isUpdate) {
        for (Contact contact : Trigger.new) {
            if (contact.Birthdate != Trigger.oldMap.get(contact.Id).Birthdate) {
                contact.SomeField__c = 'DOB Changed';
            }
        }
    }
}

Even though the trigger is updating the current Contact, we don't need to worry about recursion because:

  • The trigger will only fire on updates due to the Trigger.isUpdate check
  • The trigger will only fire when the Birthdate is changed.

If you want to limit each trigger to fire only once then what you described would be the solution. SalesForce document it here but it may be a valid scenario that triggers fire more than once in the same transaction, the issue is if it is an infinite loop. You will hit a Governor Limit if there is more than 16 recursions of the same trigger.

share|improve this answer
3  
This is true except that the trigger WILL fire a second time if you have workflows with field updates on this object. Even though workflows are run after the triggers, the old and new values are not updated yet, so the trigger will still run a second as it will see that the old value does not equal the new value. Dan Appleman has a great example of how to avoid this. I will add an answer to that effect. –  sfdc_ninja Aug 13 at 15:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.