function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
mroarkmroark 

Batch updating using the 'Mass-Updating Records with a Custom List Controller' example?

History:

 

In our environment, we have deployed a VisualForce page which allows users to mass update Opportunity records.  The code for this page mirrors the example shown in the 'Mass-Updating Records with a Custom List Controller' example in the SalesForce cookbook.  We dispense with the custom controller because we don't need to display counts to the user. (The code is included below for those who are interested).

 

Problem:

 

The problem I have is one of governer limits, specifically SOQL queries.  When I run the page in our environment, selecting more than 10 records, I get an error message informing me that too many SOQL queries were made.  Our environment does has several triggers which fire both before and after an Opportunity is updated.  I have tried refactoring these queries to limit the number of SOQL calls, using the best practices outlined in the documentation on DeveloperForce.  This helps, but I still encounter this issue if I select more than 15 records.

 

This seems to indicate that the trigger is not being fired for multiple records.  I added some debug methods to the triggers to figure out how many records were being updated when the trigger was called, and discovered the triggers were being fired for only a single Opportunity (as shown in the debug log snippet below).

 

13:46:29.141 (1141911000)|CODE_UNIT_STARTED|[EXTERNAL]|01qQ0000000CywQ|Oppty_Change on Opportunity trigger event AfterUpdate for [0067000000FXx6J]
13:46:29.142 (1142110000)|SYSTEM_METHOD_ENTRY|[17]|System.debug(ANY)
13:46:29.142 (1142166000)|SYSTEM_METHOD_ENTRY|[17]|LIST.size()
13:46:29.142 (1142188000)|SYSTEM_METHOD_EXIT|[17]|LIST.size()
13:46:29.142 (1142211000)|USER_DEBUG|[17]|DEBUG|Trigger fired for 1 records.

 

So, here is my question.  How do I force the standard controller's {!save} method to update all the records as a single batch, instead of as individual records?  Can I do this?  If not, do I have to build a custom controller which overrides the {!save} method and instead updates all the selected records?

 

Visualforce Code:

 

<apex:page standardController="Opportunity" recordSetVar="unused"
    sidebar="false">
    <apex:includeScript value="{!$Resource.UtilJS}" />
    <apex:form >
        <apex:pageBlock >
            <apex:pageMessages />
            <apex:pageBlock >
Note: All modifications made on the page will be lost if Return button is clicked without clicking the Save button first. 
</apex:pageBlock>
            <apex:pageBlockButtons >
                <apex:commandButton value="Save" action="{!save}" />
                <apex:commandButton value="Return" action="{!cancel}" />
            </apex:pageBlockButtons>
            <apex:pageBlockTable value="{!selected}" var="opp" id="table">
<apex:column headerValue="Sales Region">
<apex:inputField value="{!opp.Sales_Region__c}" />   
</apex:column>
<apex:column headerValue="Opportunity Owner">
<apex:inputField value="{!opp.OwnerID}" />   
</apex:column>
<apex:column headerValue="Opportunity Name">
                    <apex:inputField value="{!opp.name}" />

  </apex:column>
                <apex:column headerValue="Stage">
                    <apex:inputField value="{!opp.stageName}" /> 
</apex:column>
                <apex:column headerValue="Amount">
                    <apex:inputField required="true" value="{!opp.amount}" />      
                </apex:column>
                <apex:column headerValue="Forecast Category">
                    <apex:inputField value="{!opp.ForecastCategoryName}" />
</apex:column>
<apex:column headerValue="Close Date">
                    <apex:inputField value="{!opp.closeDate}" />
                </apex:column>
<apex:column headerValue="FC Updated">
<apex:inputField value="{!opp.FC_Updated__c}" />
</apex:column>
<apex:column headerValue="Mgr Lock">
<apex:inputField value="{!opp.Mgr_Lock__c}" />
</apex:column>
<apex:column headerValue="Mgr Amount">
<apex:inputField value="{!opp.Mgr_Amount__c}" />
</apex:column>
<apex:column headerValue="Mgr Forecast Category">
<apex:inputField value="{!opp.Mgr_Forecast_Category__c}" />
</apex:column>
<apex:column headerValue="Mgr Close Date">
<apex:inputField value="{!opp.Mgr_Close_Date__c}" />
</apex:column>
<apex:column headerValue="Mgr Comments">
<apex:inputField value="{!opp.Mgr_Comments__c}" />
                </apex:column>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

 

Best Answer chosen by Admin (Salesforce Developers) 
mroarkmroark

I submitted this to SalesForce support, and they confirmed that I would need to write a custom controller extension to update the Opportunities as a batch.

 

I am adding this note as a solution, in case anyone else encounters this issue.

All Answers

WizradWizrad

You haven't included any code which will help us solve your problem.

 

Show us your opportunity triggers and triggers that fire as a result of opportunity trigger.

mroarkmroark

I have included the code of one of my triggers below.  As you can see, it is built in such a way to be able to handle bulk and batch updates, so long as the trigger is fired with multiple records in the 'Trigger.new' list. The other Opportunity triggers (there are a total of three) have been refactored to also handle bulk and batch updates using the SalesForce recommended best practices.

 

My expectation is that when the 'Save' method on the Opportunity standard list controller is called (such as happens if a user clicks the 'Save' button on my Visualforce page with 15 records in the '{!selected}' list) the trigger should fire and have all 15 records passed to it via the 'Trigger.new' list.

 

However, what I am finding is that this is not occurring.  When a user has 15 records which are selected for update, the below trigger is being called 15 times, once for each record being updated.

 

I don't know if this is a bug, or if my expectations are incorrect.  If they are incorrect, how can I force the page to submit these records as a batch to the triggers?  Can I override the standard '{!save}' method, and use the Apex bulk load methods to do it?

 

trigger updateOpptyOwnerText on Opportunity (before insert, before update) {
	
    // Output the size of the list of records for which the trigger was fired.
    System.debug('Trigger fired for '+Trigger.new.size()+' records.');
    
    // Get a map of all Users
    Map<Id,User> mapUsers = new Map<Id,User> ([Select Id, FirstName,LastName from User]);
    
    // For each opportunity in the new set of records
    for (Opportunity o : Trigger.new)
    {
    	// Quick sanity check to make sure the Owner is a User
    	if (mapUsers.containsKey(o.OwnerId))
    	{
    	// Fetch the current Opportunity's Owner record from the user map created above
    	User currentOwner = mapUsers.get(o.OwnerId);
    	// update the Oppty_Owner_Name_Text__c field with the first and last names of the owner record.
     	o.Oppty_Owner_Name_Text__c = currentOwner.FirstName + ' ' + currentOwner.LastName;
    	}
    }
    
}

 

mroarkmroark

Can anyone here validate my assumptions, or should I log a ticket with SalesForce support?

mroarkmroark

I submitted this to SalesForce support, and they confirmed that I would need to write a custom controller extension to update the Opportunities as a batch.

 

I am adding this note as a solution, in case anyone else encounters this issue.

This was selected as the best answer