bulk processing patterns in Apex :
=================================
All of your Apex code should be designed to handle bulk operations.All of it-no exceptions.
1.Apex triggers are always bulk.
2.Avoid governor limits.
common trigger design :
=======================
the order of trigger execution is guaranteed to be unpredictable.
managing recursion :
=====================
public with sharing class IsProcessing
{
public static Boolean isProcessing=false;
}
public with sharing class TestIsProcessing
{
public void processLogic()
{
public void processLogic()
{
if(!IsProcessing.isProcessing)
{
IsProcessing.isProcessing=true;
//--- do processing
}
}
static testMethod void testProcessing()
{
system.assert(!IsProcessing.isprocessing);
TestIsProcessing tip =new TestIsProcessing();
tip.processLogic();
system.assert(IsProcessing.isProcessing);
}
}
}
The most common Apex pattern for dealing with recursion is by leveraging a public
static Boolean somewhere in a class.Then,either in trigger code or in a class method called by the trigger,the static Boolean is evaluated immediately when the trigger code first executes.If the value
is false,then it is immediately set to true when processing continues.
Note : static variables in apex are only global to the specific execution context that reference them.
Interface based centralized trigger dispatching architecture :
==========================================
Apex supports Interfaces,abstract classes and virtual classes.
ex : MainContactEntry.trigger
trigger MainContactEntry on Contact (before insert,before update,before delete,
after insert,after update,after delete)
{
TriggerDispatcher.entry(new TriggerDispatcher.TriggerParameters(Trigger.isBefore,
Trigger.isAfter,Trigger.isDelete,trigger.isInsert,Trigger.isUpdate,
Trigger.isExecuting,Trigger.old,Trigger.new,Trigger.oldMap,Trigger.newMap));
}
TriggerDispatchr.cls
public with sharing class TriggerDispatcher
{
// contacts
private static final String SACCOUNT='Account';
private static final String SCONTACT='Contact';
private static final String SLEAD = 'Lead';
private static final String SOPPORTUNITY ='Opportunity';
private static final String SUSER ='User';
public static ITriggerEntry activeInstance = null;
public static Map<Id,Sobject> sObjectsToUpdate =new Map<Id,Sobject>();
public interface ITriggerEntry
{
void mainEntry(TriggerParameters tp);
void inProgressEntry(TriggerParameters tp);
}
public class TriggerParameters
{
public String triggerObject {get;private set;}
public Boolean isBefore {get;private set;}
Public Boolean isAfter {get;private set;}
Public Boolean isDelete {get;private set;}
public Boolean isInsert {get;private set;}
public Boolean isUpdate {get;private set;}
public Boolean isExecuting {get;private set;}
public List<Sobject> oldList {get;private set;}
public List<Sobject> newList {get;set;}
public Map<Id,Sobject> oldMap {get;private set;}
public Map<Id,Sobject> newMap {get;set;}
public TriggerParameters(Boolean ib,Boolean ia,Boolean id,Boolean ii,Boolean iu,Boolean ie,
List<Sobject> ol,List<Sobject> nl,Map<Id,Sobject> om,map<Id,Sobject> nm)
{
this.isBefore = ib;
this.isAfter = ia;
this.isDelete = id;
this.isInsert = ii;
this.isUpdate = iu;
this.isExecuting = ie;
this.oldList = ol;
this.newList = nl;
this.oldMap = om;
this.newMap = nm;
this.triggerObject = getSobjType( ( this.oldList !=null && this.oldList.size() >0 )?
this.oldList[0] : this.newList[0] );
}
private String getSobjectType (Sobject so)
{
String retVal;
if(so instanceof Account) retVal = SACCOUNT;
else if (so instanceof Contact) retVal = SCONTACT;
else if (so instanceof Lead) retVal = SLEAD;
else if (so instanceof Opportunity) retVal= SOPPORTUNITY;
else if (so instanceof User) retVal = SUSER;
return retVal;
}
}
// Central dispatch entry
public static void entry(TriggerParameters tp)
{
if(activeInstance == null)
processWork(tp);
else
activeInstance.inProgressEntry(tp);
}
// Order of execution is controlled
private static void processWork(TriggerParameters tp)
{
if(tp.triggerObject == SContact && tp.isAfter && tp.isUpdate)
{
execute(new SyncContactToUser(), tp);
}
else if(tp.triggerObject == SUSER && tp.isAfter && tp.isUpdate)
execute(new SyncUserToContact(), tp);
//update records
if(sObjectsToUpdate.size() >0)
update sObjectsToUpdate.values();
}
private static void execute (ITriggerEntry ite, TriggerParameters tp)
{
activeInstace = ite;
activeInstance.mainEntry(tp);
}
}
Note :
The instanceof keyword allows you to check the if an object is an instance of a class.
SyncContactToUser.cls :
========================
public with sharing class SyncContactToUser implements TriggerDispatcher.ITriggerEntry
{
public void inProgressEntry(TriggerDispatcher.TriggerParameters tp)
{
// confirm caller origin
system.assert(tp.triggerObject=='User');
}
public void mainEntry(TriggerDispatcher.TriggerParameters tp)
{
Map<Id,Contact> newMap =(Map<Id,Contact>) tp.newMap;
List<Id> userIds = new List<Id>();
for(Contact c : newMap.values())
{
userIds.add(c.User__c);
}
Map<Id,User> userMap = new Map<Id,User>
([SELECT Id,Phone,MobilePhone,Fax FROM User WHERE Id IN : userIds]);
for(Contact c : newMap.values())
{
User u=userMap.get(c.User__c);
if(u.Phone !=c.Phone || u.MobilePhone !=c.MobilePhone || u.Fax !=c.Fax)
{
u.Email=c.Email;
u.Phone=c.Phone;
u.MobilePhone =c.MobilePhone;
u.Fax = c.Fax;
TriggerDispatcher.sObjectsToUpdate.put(u.Id,u);
}
}
}
}
SyncUserToContact.cls :
=========================
public with sharing class SyncUserToContact implements TriggerDispatcher.ITriggerEntry
{
public void inProgressEntry(TriggerDispatcher.TriggerParameters tp)
{
system.assert(tp.triggerObject == 'Contact');
}
public void mainEntry(TriggerDispatcher.TriggerParameters tp)
{
Map<Id,User> newMap=(Map<Id,User>) tp.newMap;
List<Contact> contactList = [SELECT User__c,Phone,MobilePhone,Fax FROM Contact WHERE User__c IN : newMap.keyset()];
for(Contact c : contactList)
{
User u = newmap.get(c.User__c);
if(c.Phone!=u.Phone || c.MobilePhone !=u.MobilePhone || c.Fax!=u.Fax)
{
c.Phone=u.Phone;
c.MobilePhone=u.MobilePhone;
c.Fax=u.Fax;
TriggerDispatcher.sObjectsToUpdate.put(c.Id,c);
}
}
}
}
Asynchronous Execution :
==========================
The three available tools that you can leverage for Asychronous Execution on force.com.
1.Scheduled Apex
2.Batch Apex
3.@Future calls
Through the standard interface(declarative tool) , you can activate schedulable Apex classes
and configure the execution recurrence.
The available recurrence options are very flexible,permitting schedules such as execute every Monday,Wednesday and Friday at 5 p.m. or execute at 11 a.m on third Thrusday of every month.
Limitation to use the standard interface to schedule Apex classes.You're limited to one execution per day per configured schedule.
Note : 25 is the maximum number of Apex classes that can be schedule at any one time,this poses a real problem.
if you schedule your Apex classes to run from within Apex that can execute on the hour if needed.
Scheduled Apex is only targeted to execute on a set schedule,but depending on available resources,they execute after the scheduled time.And only 25 concurrent Scheduled Apex processes
maybe active at any one time regardless of their recurrence frequency.
Future calls :
==============
Within a single execution context, we can only call 10 future methods and within each future method we're limited to 10 callouts.
1.Runs in its own execution context
2.Certain limits are increased
3.Limited to 10 calls per context
4.Limited to 200 calls per salesforce
user license,per 24 hours
5.Can't be called from Batch Apex or
future method.
Batch Apex :
==============
1.Runs in its own execution context.
2.Certain limits are increased.
3.can process up to 50 million records.
4.Batch sizes of 1-2000 records
5.Up to 250,000 daily Batch Apex Executions permitted.
6.Maximum 5 concurrent batch Apex processes.
7.Transactions cannot span batch processes in Apex.
In other words, after each batch of 1 to 2,000 objects has been committed, this cannot be undone in a subsequent batch.
8.Can't be called from Batch Apex or future methods.
There are a few ways to work around this using other techniques such as email services and chaining by way of using Scheduled Apex to bridge subsequent Batch Apex calls.
No comments:
Post a Comment