How to manage Salesforce Governor Limits?

  1. Avoid SOQLs,DMLs inside loops, otherwise the query,DML will execute how many time the loop iterates.
  2. Provide as many as required conditions while executing the main functionality.
  3. Use RemoteAction function to make the transaction as asynchronous.
  4. Use future annotation to make the transaction asynchronous, but this approach is very costly. We have limited Future calls for transaction.
  5. Provide NULL POINTER checks before executing SOQL,DML statements.
  6. Check whether there is any recursive functionality. If any, avoid those so that we can reduce numbers of SOQLs,DMLs called.
  7. Keep required where conditions while executing the SOQL, so that we can get proper result. this can be useful to overcome 50001 query rows.

Examples where we made mistakes while implementing apex code

/**
* Trigger Name: ContactAfterInsert
* Description: When Contact gets successfully inserted, Update the corresponding account. Concatenate contact Id to the account custom field called contactIds__c.
*/
trigger ContactAfterInsert on Contact (after insert){
for(Contact contact: trigger.new){
Account account = [select id from account where id =: contact.accountId];
String existingAccountConts = account.contactIds__c;
// contactIds__c is the custom field in account which contains all the contact ids for that account with separation of colon(:).
account.contactIds__c = existingAccountConts +":"+ contact.Id;
update account;
}
}

Note: The main thing we have remember is every trigger should be BULKIFIED, here the trigger is not BULKIFIED And we written the SOQL and DML inside for loop. Suppose we have 160 Contact records in trigger.new(),so obviously the loop iterates 160 times and SOQLs and DMLs will executes 160 times.
as per the Governor limits we have to execute only 100 SOQLs and 150 DMLs records.

How to BULKIFY the above trigger

/**
* Trigger Name: ContactAfterInsert
* Description: When Contact gets successfully inserted, Update the corresponding account. Concatenate contact Id to the account custom field called contactIds__c.
*/
trigger ContactAfterInsert on Contact (after insert){
List<Id> accountIdsList = new List<Id>();
List<Account> updateAccountList = new List<Account>();
for(Contact contact: trigger.new){
accountIdsList.add(contact.accountId);
}
// NULL check before executing the query..
if(!accountIdsList.isEmpty()){
List<Account> accountList = [select id,contactIds__c from account where id IN: accountIdsList];
}
// NULL check before executing the functionality...
if(!accountList.isEmpty()){
for(Account account: accountList){
for(Contact contact: trigger.new){
if(account.Id == contact.accountId){
String existingAccountConts = account.contactIds__c;
// contactIds__c is the custom field in account which contains all the contact ids for that account with separation of colon(:).
account.contactIds__c = existingAccountConts +":"+ contact.Id;
updateAccountList.add(account);
}
}
}
}
if(!updateAccountList.isEmpty()){
update updateAccountList;
}
}

Example how to resolve SOQL 101 using RemoteAction Annotation.

we have a vf page and class are like this.
// Controller
/***********************************************************
* Class Name: CreateNewOpportunityController
* Description: CreateNewOpportunityController is the controller class to capture the opportunity details
***********************************************************/

public class CreateNewOpportunityController{
// Constructor of class
Public String opptyId{get;set;}
public CreateNewOpportunityController(ApexPages.StandardController Controller){
// Constructor Code....
}
// Method to save the Opportunity record and submit to the approval process.
public PageReference SaveAndSubmit() {
SaveOpportunity();
SubmitOpportunity();
// Once submission is completed page refrence to the details page.
pageReference detailOpportunity = new pageReference('/apex/OpportunityDetailPage?id='+opptyId);
detailOpportunity.setRedirect(true);
return detailOpportunity;
}
SaveOpportunity(){
// Code to save the opportunity
Insert opportunityRecord;
opptyId = opportunityRecord.Id;
}
SubmitOpportunity(){
// Code to Submit the Opportunity
}
}

Visual Force page

 

<!------------------------------------------
Page Name: CreateNewOpportunityPage
Description:CreateNewOpportunityController is the controller class to capture the opportunity details
---------------------------------------------->

<apex:page standardController="Opportunity" tabStyle="FundClaim__c" extensions="CreateNewOpportunityController" id="pg">
<!-- create some table to enter opportunity details save and submit the opportunity... -->
<apex:form>
<apex:inputField id="Name" value="{!opportunity.Name}"/>
<apex:inputField id="Stage" value="{!opportunity.stage}"/>
</apex:form>
<apex:commandButton id="saveandSubmit" value="saveandSubmit" action="{!SaveAndSubmit}"/>
</apex:page>

Note: we have so many triggers on opportunity insert and update, if we click on save and submit so that all the triggers will fire and executed as single transaction and may result to through an error 101SOQL OR Too Many DML Statements. Just see the below code how to use remote action and make the transaction as asynchronous.

Visual Force page

<!------------------------------------------
Page Name: CreateNewOpportunityPage
Description:CreateNewOpportunityController is the controller class to capture the opportunity details
---------------------------------------------->

<apex:page standardController="Opportunity" tabStyle="FundClaim__c" extensions="CreateNewOpportunityController" id="pg">
<!-- Keep the jQuery lib file in static resources and use that in the page to use jQuery functions (static resource name is jQueryLib )-->
<script src="{!URLFOR($Resource.jQueryLib)}" type="text/javascript"></script>
<!-- create some table to enter opportunity details save and submit the opportunity... -->
<apex:form>
<apex:inputField id="Name" value="{!opportunity.Name}"/>
<apex:inputField id="Stage" value="{!opportunity.stage}"/>
</apex:form>
<apex:commandButton id="saveandSubmit" value="saveandSubmit" action="{!SaveAndSubmit}"/>
<!-- Script to submit the Opportunity through RemoteAction -->
j$(function(){
if('{!opptyId}'!='' && '{!opptyId}'!= null){
CreateNewOpportunityController.SubmitOpportunity('{!opptyId}',function(res,eve){
if(eve.status){
var displayMessage = res.split(',');
if(displayMessage[0]=='Success'){
window.top.location.href= '/apex/OpportunityDetailPage?id='+opptyId;
}
}
}
}
}
</apex:page>

Controller

/***********************************************************
* Class Name: CreateNewOpportunityController
* Description: CreateNewOpportunityController is the controller class to capture the opportunity details
***********************************************************/

public class CreateNewOpportunityController{
// Constructor of class
Public String opptyId{get;set;}
public CreateNewOpportunityController(ApexPages.StandardController Controller){
// Constructor Code
}
// Method to save the Opportunity record and submit to the approval process.
public PageReference SaveAndSubmit() {
SaveOpportunity();
}
SaveOpportunity(){
// Code to save the opportunity
Insert opportunityRecord;
opptyId = opportunityRecord.Id;
}
@remoteAction
public static string SubmitOpportunity(String opptyId){
String returnString;
Opportunity opportunity = [select id,stage from Opportunity where id =: opptyId];
// Code to Submit the Opportunity
if(any errors while submitting){
returnString = 'Fail,'+ 'Error message';
}
else{
returnString = 'Success,'+'Success message';
}
return returnString;
}
}

–> The above code will work as two transactions one is for save and one for submit, So the Governor limits will applicable for save and submit differently.

——————————————————————————————————–
How Future Annotation is used to make the transaction as asynchronous
let say we have same above scenario, save and submit opportunity

Controller

/***********************************************************
* Class Name: CreateNewOpportunityController
* Description: CreateNewOpportunityController is the controller class to capture the opportunity details
***********************************************************/

public class CreateNewOpportunityController{
// Constructor of class
Public String opptyId{get;set;}
public CreateNewOpportunityController(ApexPages.StandardController Controller){
// Constructor Code....
}
// Method to save the Opportunity record and submit to the approval process.
public PageReference SaveAndSubmit() {
SaveOpportunity();
SubmitOpportunity(opptyId);
// Once submission is completed page refrence to the details page.
pageReference detailOpportunity = new pageReference('/apex/OpportunityDetailPage?id='+opptyId);
detailOpportunity.setRedirect(true);
return detailOpportunity;
}
SaveOpportunity(){
// Code to save the opportunity
Insert opportunityRecord;
opptyId = opportunityRecord.Id;
}
SubmitOpportunity(opptyId){
Opportunity opportunity = [select id,stage from Opportunity where id =: opptyId];
// Code to Submit the Opportunity
}
}

Visual Force page

 

<!------------------------------------------
Page Name: CreateNewOpportunityPage
Description:CreateNewOpportunityController is the controller class to capture the opportunity details
---------------------------------------------->

<apex:page standardController="Opportunity" tabStyle="FundClaim__c" extensions="CreateNewOpportunityController" id="pg">
<!-- create some table to enter opportunity details save and submit the opportunity... -->
<apex:form>
<apex:inputField id="Name" value="{!opportunity.Name}"/>
<apex:inputField id="Stage" value="{!opportunity.stage}"/>
</apex:form>
<apex:commandButton id="saveandSubmit" value="saveandSubmit" action="{!SaveAndSubmit}"/>
</apex:page>

Problems of this approach

  • This approach is very costly, because we have limited number of future call per transaction and for the instance.
  • This call may not done immediately, it depends on how many batch, scheduler and future call are in queue.
  • Some times future call takes 2 or 3 days also to run the functionality. So is there any independent functionality and check there is no business impact on this, we can use this method.
    Ex: If we want to share records to some optional users in the organization and that should not be done immediately. In that case we go for this approach to keep the sharing functionality oin future call.