6

Below is my code snippet that I have used. And also I used static Boolean to handle the recursive call.

But when I update more than 200 records then trigger only process first 200 record not all the records after 200.

So I think this is not a best solution to use static Boolean variable.

So what will be the best solution for this ?

trigger RN_Account on Account (after update) 
{
    RN_AccountHandler handler = new RN_AccountHandler();
    if(Trigger.isAfter && Trigger.isUpdate && RN_AccountHandler.isUpdate)
    {
        RN_AccountHandler.isUpdate = false;
        handler.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
    }
}

Handler

public with sharing class RN_AccountHandler 
{
    public static Boolean isUpdate = true;
    public void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount)
    {
        system.debug('========mapNewAccount.values()============'+mapNewAccount.values().size());
    }
}

I have added RN_AccountHandler.isUpdate to handle the

enter image description here

  • Are you firing this trigger by running some code in anonymous window? – javanoob Mar 28 '16 at 14:24
  • @javanoob yes I am testing from anonymous window.. – Ratan Paul Mar 28 '16 at 14:27
  • Use a static map of IDs – cropredy Mar 28 '16 at 14:28
  • If your anonymous code is sending 400 records for update, trigger process first 200 and assigns the flag value to false and the next set of 200 records are skipped. There was an excellent post by @KeithC where he explained this problem clearly. If I find link, I will post here. – javanoob Mar 28 '16 at 14:30
  • @javanoob yeah sure.. – Ratan Paul Mar 28 '16 at 14:30
5

So as @cropredy suggested using static map instead this is what I tried. I used static set to solve this issue.

I can't say this is the best solution or not but this is working.

may me some usecases will be there please feel free to comment on this post.

So I used set of Id static variable.

Trigger

trigger RN_Account on Account (after update) 
{
   RN_AccountHandler handler = new RN_AccountHandler();

   if(Trigger.isAfter && Trigger.isUpdate && !RN_AccountHandler.setAccountIds.containsAll(Trigger.newMap.KeySet())) 
   { 
      RN_AccountHandler.setAccountIds.addAll(Trigger.newMap.KeySet());
      handler.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
    }
}

Handler

public with sharing class RN_AccountHandler 
{   
    public static set<Id> setAccountIds = new set<Id>();

    public void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount)
    {
       system.debug('====11========='+mapNewAccount.values().size());
       update [SELECT Id, Name FROM Account WHERE Id IN: mapNewAccount.keyset()];
    }
}

One more solution

IN controller after execution just make the boolean to true

Trigger

trigger RN_Account on Account (after update) 
{
    RN_AccountHandler handler = new RN_AccountHandler();
    if(Trigger.isAfter && Trigger.isUpdate && RN_AccountHandler.isUpdate)
    {
        RN_AccountHandler.isUpdate = false;
        handler.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
    }
}

Controller

public with sharing class RN_AccountHandler 
{
    public static Boolean isUpdate = true;
    public void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount)
    {
        system.debug('============'+mapNewAccount.values().size());
        update [SELECT Id, Name FROM Account WHERE Id IN: mapNewAccount.keyset()];
        RN_AccountHandler.isUpdate = true;
    }
}
  • 1
    We would need this workaround only when trying to update more than 200 records from anonymous window right? For other situations, your original code from the post should work fine. – javanoob Mar 29 '16 at 16:23
  • 1
    @javanoob exactly.. my above will work for less than 200 records with recursive and this above will work for more than 200 records with recursive situation. – Ratan Paul Mar 29 '16 at 16:25
  • @sfdcweb, my only doubt is if the collection of records in the first recursion call is always the same as in the first batch. Let's say SF executes after trigger for the first 200 batch, it triggers another execution of the trigger with the same set of records, right? – edelrabe Mar 29 '16 at 19:26
  • I'm not sure about order of execution of triggers when records updated in batches. Recursive calls are fired when all records are updated or when first batch is updated? – edelrabe Mar 29 '16 at 19:36
  • 1
    sfdcweb -- (1) I don't follow the 'one more solution'; (2) I think I would let the handler decide whether to reprocess rather than the trigger. (3) things get more complicated if you allow for partial successes in the DML. see cropredysfdc.com/2015/04/26/245 for a discussion - you would need to actually clear the static set of ids for the partial success records as they will be replayed by SFDC. (4) Of course, your best alternative might be to reengineer whatever does the >200 DML operation and break into multiple transactions – cropredy Mar 30 '16 at 16:46
1

The only approach I know is to store a Set of Ids that have been processed already as @cropredy suggested. So the code will be something like this:

trigger RN_Account on Account (after update) 
{
    RN_AccountHandler handler = new RN_AccountHandler();
    if(Trigger.isAfter && Trigger.isUpdate)
    {
        RN_AccountHandler.isUpdate = false;
        handler.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
    }
}

public with sharing class RN_AccountHandler 
{
    public static Set<Id> processedIDs = new Set<Id>();
    public void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount)
    {
        system.debug('========mapNewAccount.values()============'+mapNewAccount.values().size());
        for (Id accountId : mapNewAccount.keySet()) {
            if (!processedIDs.contains(accountId)) {
                // your code
                processedIDs.add(accountId);
            }
        }
    }
}
  • Yes exactly... This is the way i found yesterday... hope someone have better idea than this. else I will accept this ans.. – Ratan Paul Mar 29 '16 at 8:43
  • Yesterday I was thinking to use static set<Id> and use addAll method to add all the values and check containsAll to check each batch ids exist or not. this can be a way but I think if only one record updates instead chunk records then it will may not work.. – Ratan Paul Mar 29 '16 at 9:34
0

The issue is that you've put your public static boolean in your class instead of in your trigger. Your class is called from the trigger and needs to be called for every 200 records when the trigger is called the FIRST time. By putting the Boolean in your class, your preventing the class from running more than once per trigger invocation. Relocate your boolean as follows using an external trigger helper class to hold the boolean.

trigger RN_Account on Account (after update) 
{
   if(!triggerHelper.FirstRun){
      FirstRun = true; 
      RN_AccountHandler handler = new RN_AccountHandler();
      if(Trigger.isAfter && Trigger.isUpdate)
      {
          handler.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
      }
    }
}


public with sharing triggerHelper class {

   public static boolean FirstRun = false;

}

When the trigger runs the SECOND time, the boolean will be true, causing the trigger to immediately exit.

  • crmprogdev.. nop it is still not processing more than 200 records(next chunk records). – Ratan Paul Mar 28 '16 at 14:52
  • Did you catch the last edit and remove the handler class boolean from your handler class as well as the handler class boolean from your trigger? – crmprogdev Mar 28 '16 at 14:53
  • Yes now I removed RN_AccountHandler.isUpdate = false; from trigger but it is processing only first 200 records.. let me updated question I also tried with removing RN_AccountHandler.isUpdate boolean from trigger if condition.. still the same. – Ratan Paul Mar 28 '16 at 15:00
  • Okay, you removed the && RN_AccountHandler.isUpdate? Try removing the with sharing from the trigger helper class. Are you certain you have more than 200 records? Does system debug confirm that? Ahh, wait a min, I see an error, it should be if(!triggerHelper.FirstRun). Oh, you had caught that. Wouldn't have compiled. – crmprogdev Mar 28 '16 at 15:04
  • crmprogdev.. Yes I removed RN_AccountHandler.isUpdate and testing I have updated the same in my question. Pls check.. let me know If I am doing anything wrong.. Also added screenshot. how I am testing and in my org 999 records present.. – Ratan Paul Mar 28 '16 at 15:11
0

You can do this:

public class RN_AccountHandler
{
    public static boolean isExecuting = false;

    public static void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount)
    {
        if( RN_AccountHandler.isExecuting )
        {
            // avoid recursion: if was executed during the same context 
            return;
        }

        RN_AccountHandler.isExecuting = true;

        // your code for after update logic
    }
}
  • Oleksiy... have you tested this ans.. It looks same like above mine.. you are just added isExecuting inside method.. – Ratan Paul Mar 28 '16 at 17:03

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.