0

I have a trigger handler as shown below (Please Ignore any Syntax errors)

trigger AccountTrigger on Account (after delete, after insert, after undelete, after update, before delete, before insert, before update){
AccountTriggerHandler handler = new AccountTriggerHandler();

    if(Trigger.isInsert && Trigger.isBefore){
    }
    // After Insert 
    else if(Trigger.isInsert && Trigger.isAfter){
        handler.OnAfterInsert(Trigger.new, Trigger.oldMap);
    }
    // Before Update 
    else if(Trigger.isUpdate && Trigger.isBefore){
        if(CheckAccountRecursive.runBeforeUpdateOnce()){
            handler.OnBeforeUpdate(Trigger.new, Trigger.oldMap);
        }
    }
    // After Update 
    else if(Trigger.isUpdate && Trigger.isAfter){
        if(CheckAccountRecursive.runAfterUpdateOnce()){
            handler.OnAfterUpdate(Trigger.new, Trigger.oldMap, Trigger.newMap);
        }
    }
}

I handler looks like this. I'm trying to reduce the number of SOQLs

public class AccountTriggerHandler{
public static Map<id,Account> AccountMap;
public AccountTriggerHandler(){
    AccountMap = [SOQL QUERY] 
}
Static {
    AccountMap = new Map<Id,Account>();       
}
/*
public void OnBeforeInsert(){
    // EXECUTE BEFORE INSERT LOGIC
}
*/
public void OnAfterInsert(List<Account> newAcc, Map<Id,Account> oldAccMap){
    AccountTriggerHandler.dosomeInsert(newAcc,OldAccMap);
}
/*    
@future public static void OnAfterInsertAsync(Set<ID> newAccsIDs){

}
*/
public void OnBeforeUpdate(List<Account> newAcc, Map<Id,Account> oldAccMap){
    AccountTriggerHandler.doSomeUpdate(newAcc,OldAccMap);
}

 public void OnAfterUpdate(List<Account> newAcc, Map<Id,Account> oldAccMap, Map<Id,Account> newAccMap){
    AccountTriggerHandler.someMethod1(newAcc,OldAccMap);
    AccountTriggerHandler.someMethod2(newAcc,OldAccMap);
    AccountTriggerHandler.someMethod3(newAcc,OldAccMap);
    AccountTriggerHandler.someMethod4(newAcc,OldAccMap,NewAccMap);
}
/*
@future public static void OnAfterUpdateAsync(Set<ID> updatedAccsIDs){

}

public void OnBeforeDelete(Account[] AccsToDelete, Map<Id, Account> AccsMap){
    // BEFORE DELETE LOGIC
}

public void OnAfterDelete(Account[] deletedAccs, Map<Id, Account> AccsMap){
    // AFTER DELETE LOGIC
}

@future public static void OnAfterDeleteAsync(Set<ID> deletedAccIDs){

}
public void OnUndelete(Account[] restoredObjects){
    // AFTER UNDELETE LOGIC
}
*/
//Secondary Account Owner Update
public static void dosomeinsert(List<Account> newAccs, Map<Id,Account> oldAccMap){
    do some thing with AccountMap(SOQL) from constructor
}
public static void method1(List<Account> newAccs, Map<Id,Account> oldAccMap){
  do some thing with AccountMap(SOQL) from constructor  
}
|
|
|
So On
}

My question:

I know it is not a best practice to write a SOQL in constructor. My use case is to get subqueries(child records) & store it on "AccountMap" & use that in below methods.

  1. As im initializing the handler in trigger and using that in afterinsert, beforeupdate....trigger events - what would be the SOQL count here?

  2. Does the constructor gets called just once or get called everytime for each method reference handler.OnAfterInsert, handler.OnBeforeUpdate, handler.OnAfterUpdate?

  3. What would be the difference if i use the same soql with in methods on handler itself like public void OnBeforeUpdate{ SOQL}?

Please suggest me a best way to optimize my code and reduce the SOQL count

  • I have edited your post to clearly call out your questions, if that's not correct, please feel free to edit it appropriately. – Jayant Das Mar 13 at 21:01
1

You call your constructor only once, at the top of your trigger:

AccountTriggerHandler handler = new AccountTriggerHandler();

As a result, SOQL queries in your handler's constructor will execute once per trigger event per 200 records in the transaction. If you update 200 records, you will use two SOQL queries (one for before insert and one for after insert). If you insert 400 records, you will use four SOQL queries (one for before insert and one for after insert for each 200-record batch).

Because your use case is to obtain child records of the records in scope for your trigger, you need to be running the query at the time you are running it, and I don't see a way for you to reduce this SOQL count. If you're running into limits trouble, you may need to look elsewhere in your code.

You do seem to have some confusion in your code as to whether you're operating in a static context or not. These methods declared static:

public static void dosomeinsert(List<Account> newAccs, Map<Id,Account> oldAccMap){
    do some thing with AccountMap(SOQL) from constructor
}

cannot access any instance variables - non-static members of your class - because static methods aren't bound to a class instance. You've not shown us the declaration of AccountMap, but I'm assuming it is a non-static member variable.

I would recommend that you decide to either use static methods or non-static methods for these "worker" calls, but not switch between the two unless you have a clear reason to do so.

  • yeah, i did declare AccMap as static across the board & thats the way Im able to access it. "you need to be running the query at the time you are running it" - by this do you mean to have the same SOQL for each method in handler like public void OnBeforeInsert{ AccMap SOQL } public void OnAfterInsert{ AccMap SOQL } – Sam Mar 13 at 19:59
  • @Sam I think you need to edit your question with some more details and real code before anyone can answer that. Generally I would say initializing a static variable in a constructor is a bad idea. – David Reed Mar 13 at 19:59
  • Will Update now David & thanks for your feedback/response. Correct me If im wrong for below. "you need to be running the query at the time you are running it" - by this do you mean to have the same SOQL for each method in handler like public void OnBeforeInsert{ AccMap SOQL } public void OnAfterInsert{ AccMap SOQL } – Sam Mar 13 at 20:03
  • No, that is not what I mean. I do not understand your use case well enough to answer that one way or the other. – David Reed Mar 13 at 20:04
0

Based on what I get from your questions:

  1. As im initializing the handler in trigger and using that in afterinsert, beforeupdate....trigger events - what would be the SOQL count here?

You have got this clarified in David's answer. If you are referring to the SOQL count that you are making in your trigger handler, then it will be per trigger execution. Every time the trigger processes a batch of record, the SOQL will be called.

  1. Does the constructor gets called just once or get called everytime for each method reference handler.OnAfterInsert, handler.OnBeforeUpdate, handler.OnAfterUpdate?

Constructors are always called whenever you are creating an instance of an object. So if you create only one instance, it will be called only once. But if you create further instances of the class, it will be called as many times. Refer more on Constructors on documentation.

  1. What would be the difference if i use the same soql with in methods on handler itself like public void OnBeforeUpdate{ SOQL}?

There will be not much of a difference except that you will end up repeating to write the same code in every method. Assuming all your methods are instance methods (and not a mix of instance and class methods as your code also reflects static methods), you can run the SOQL in constructor, populate the variable and then utilize that in your instance methods. Note that having a SOQL in constructor is not a bad practice, it just depends how effectively you are utilizing it.


In its current form except for removing your static declared methods in trigger handler to be able to use an instance variable assigned in constructor, I don't really see a need here for further optimization.

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.