Tuesday, August 17, 2010

Bulk Operations in SalesForce, Apex Code

When a developer is writing a piece of software that interacts with a standard database outside of the cloud, the only limits that he typically considers are memory, hard drive space, and processor capability. When developing on the Salesforce.com platform, however, you have to seriously consider the "cloud limitations."

What I refer to as "cloud limitations" are in fact the Salesforce Governor Limits. The Governor Limits serve as a type of security rule more than an actual limitation on the servers, and they help protect Salesforce from bad code written by a a developer that isn't resource conscious.

Even though they serve a useful purpose, the Governor Limits are really tough and really force a developer to alter how he writes software, especially Apex Triggers. Take for example the limit on DML statements (insert, update, upsert, etc.) of 20, a non-cloud acclimated programmer would scoff at the idea of only being able to do 20 inserts or updates in the entire scope of his software. The same goes for SOQL queries, you cannot exceed the limit of 20. While you may think that 20 DML statements are more than enough for your standard Apex Trigger which updates one record at a time, what happens when many records are updated all at once, calling that trigger dozens of times? This is where you have to start thinking about staying under the limits, otherwise your code will not work properly.

The first thing you need to understand about bulk operations is that a trigger can be called for several records. For example, an 'after delete' trigger will process many records if a user chooses to delete many items at once.

If your trigger does not take this fact into account, and you perform an SOQL statement for each affected record, then the Governor Limits will cause an error to occur if there are more than 20 affected records. This could cause major problems for your Org. Thankfully, solving this turns out to be really simple.

The solution is to use Apex Collections, these include Lists, Sets, and Maps. These collections help us store large amounts of information about the changes we have to make in a single variable, allowing us to perform a single operation on the entire collection versus one operation for each. Here's are two examples, first a non-bulkified, followed by a bulkified solution.




// Insert three rows one at a time, counts as 3 DML statements against the limits
Insert row1;
Insert row2;
Insert row3;

// Insert three rows all at once, counts as only 1 DML statement against the limits
List rows = new List();
rows.add(row1);
rows.add(row2);
rows.add(row3);
Insert rows;

As you can see, a simple Apex List allows us to add many values that will be updated or inserted, then we can perform the required DML statement on the List instead of each individual element.

The other two Apex collections are Sets and Maps. Sets are unordered collections of information, and give you a bit of flexibility when it comes to searching for data, and Maps are particularly useful if you need to store a key-value pair.

Now let's apply the collection logic to the trigger as a whole.




for(sObject TriggeredVal : Trigger.new){

First you have to realize that this loops cycles through all affected records sent to the trigger. Instead of doing any SOQL or DML statements inside this loop, you can check for your trigger criteria, and add any records, ids, or criteria that you need to query for or update to your collection.

After this, you can perform SOQL statements based on your collection, using the IN statement.

Ex.



trigger TestTrigger on Account (after update) {
/********************************************************************
* Variable initialization
*********************************************************************/

Set ownerIds = new Set();
List affectedCon = new List();
Integer loopIn;

/********************************************************************
* Trigger criteria implementation
* We will check if the account type is Individual and set
* their contacts description to test
*********************************************************************/

loopIn = 0;
for(Account TriggeredAcc : Trigger.new){
if(TriggeredAcc.Type == 'Individual'){//check if type is individual
ownerIds.add(TriggeredAcc.Id);
}

loopIn++;
affectedCon[loopIn].Description = 'Test';//change the field value
loopIn++;

/********************************************************************
* Bulk Processing
*********************************************************************/
loopIn = 0;
affectedCon = [SELECT Id, Description FROM Contact WHERE AccountId in: ownerIds];//get the contacts for the accounts listing

while(loopIn <>

5 comments:

  1. Hi, Thanks for the post. Any chance there is some extra code in the code block? It seems as if it may have been cut off.

    ReplyDelete
  2. Great article. I can also add that for me vdr secure file sharing
    is very convenient. I use t everyday.

    ReplyDelete
  3. This is really great information found here, I really like your blog. Thanks very much for the share. Keep posting.

    _______________________
    Total video downloader for Mac free

    ReplyDelete
  4. I don’t waste my free time in watching movies except I go for to read posts on net and get updated from newest technologies.
    Boekhouder zzp

    ReplyDelete
  5. This is very interesting to read such an amazing articles. Whole blog was really an awesome site which I have never found anywhere. PixelStyle Photo Editor for Mac

    ReplyDelete