由于Salesforce本身的Limitation,当然也出于程序员自身的修养,我们应该在编码过程中遵循一定的规范,让我们的代码整洁高效。下面的链接是我们平时写代码时应该遵循的一些最佳实践
,我们这边整理一下以加深理解。还有就是如果你的代码之后交付时是需要打包上传到AppExchange的,如果不遵循这些最佳实践的话,你的代码是通过不了Security Review的。
Best Practice #1: Bulkify your Code
只处理一条数据的代码:
trigger accountTestTrggr on Account (before insert, before update) {
//This only handles the first record in the Trigger.new collection
//But if more than one Account initiated this trigger,
//those additional records
//will not be processed
Account acct = Trigger.new[0];
List<Contact> contacts = [select id, salutation, firstname, lastname, email
from Contact where accountId = :acct.Id];
}
处理多条数据:
trigger accountTestTrggr on Account (before insert, before update) {
List<String> accountNames = new List<String>{};
//Loop through all records in the Trigger.new collection
for(Account a: Trigger.new){
//Concatenate the Name and billingState into the Description field
a.Description = a.Name + ':' + a.BillingState
}
}
Best Practice #2: Avoid SOQL Queries or DML statements inside FOR Loops
查询和DML操作在循环之内:
trigger accountTestTrggr on Account (before insert, before update) {
//For loop to iterate through all the incoming Account records
for(Account a: Trigger.new) {
//THIS FOLLOWING QUERY IS INEFFICIENT AND DOESN'T SCALE
//Since the SOQL Query for related Contacts is within the FOR loop, if this trigger is initiated
//with more than 100 records, the trigger will exceed the trigger governor limit
//of maximum 100 SOQL Queries.
List<Contact> contacts = [select id, salutation, firstname, lastname, email
from Contact where accountId = :a.Id];
for(Contact c: contacts) {
System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '],
LastName[' + c.lastname +']');
c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
//THIS FOLLOWING DML STATEMENT IS INEFFICIENT AND DOESN'T SCALE
//Since the UPDATE dml operation is within the FOR loop, if this trigger is initiated
//with more than 150 records, the trigger will exceed the trigger governor limit
//of 150 DML Operations maximum.
update c;
}
}
}
把查询和DML操作放在循环之外:
trigger accountTestTrggr on Account (before insert, before update) {
//This queries all Contacts related to the incoming Account records in a single SOQL query.
//This is also an example of how to use child relationships in SOQL
List<Account> accountsWithContacts = [select id, name, (select id, salutation, description,
firstname, lastname, email from Contacts)
from Account where Id IN :Trigger.newMap.keySet()];
List<Contact> contactsToUpdate = new List<Contact>{};
// For loop to iterate through all the queried Account records
for(Account a: accountsWithContacts){
// Use the child relationships dot syntax to access the related Contacts
for(Contact c: a.Contacts){
System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '], LastName[' + c.lastname +']');
c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
contactsToUpdate.add(c);
}
}
//Now outside the FOR Loop, perform a single Update DML statement.
update contactsToUpdate;
}
Best Practice #3: Bulkify your Helper Methods
就是我们平常所写的一些helper方法或者是公共方法处理的要是集合形式(Array, List, Set, ect.)。
Best Practice #4: Using Collections, Streamlining Queries, and Efficient For Loops
Use collections inefficiently:
trigger accountTrigger on Account (before delete, before insert, before update) {
//This code inefficiently queries the Opportunity object in two seperate queries
List<Opportunity> opptysClosedLost = [select id, name, closedate, stagename
from Opportunity where
accountId IN :Trigger.newMap.keySet() and StageName='Closed - Lost'];
List<Opportunity> opptysClosedWon = [select id, name, closedate, stagename
from Opportunity where
accountId IN :Trigger.newMap.keySet() and StageName='Closed - Won'];
for(Account a : Trigger.new){
//This code inefficiently has two inner FOR loops
//Redundantly processes the List of Opportunity Lost
for(Opportunity o: opptysClosedLost){
if(o.accountid == a.id)
System.debug('Do more logic here...');
}
//Redundantly processes the List of Opportunity Won
for(Opportunity o: opptysClosedWon){
if(o.accountid == a.id)
System.debug('Do more logic here...');
}
}
}
Use collections efficiently:
trigger accountTrigger on Account (before delete, before insert, before update) {
//This code efficiently queries all related Closed Lost and
//Closed Won opportunities in a single query.
List<Account> accountWithOpptys = [select id, name, (select id, name, closedate,
stagename from Opportunities where accountId IN :Trigger.newMap.keySet()
and (StageName='Closed - Lost' or StageName = 'Closed - Won'))
from Account where Id IN :Trigger.newMap.keySet()];
//Loop through Accounts only once
for(Account a : accountWithOpptys){
//Loop through related Opportunities only once
for(Opportunity o: a.Opportunities){
if(o.StageName == 'Closed - Won'){
System.debug('Opportunity Closed Won...do some more logic here...');
}else if(o.StageName =='Closed - Lost'){
System.debug('Opportunity Closed Lost...do some more logic here...');
}
}
}
}
Best Practice #5: Streamlining Multiple Triggers on the Same Object
对同一个Object最好Trigger都写在一个类里面,这样可以有效避免多次查询和DML操作。
Best Practice #6: Querying Large Data Sets
查询通常需要加上限制条件,来减少查询的数量。
未加限制条件:
//A runtime exception is thrown if this query returns
//enough records to exceed your heap limit.
Account[] accts = [SELECT id FROM account];
加限制条件:
// Use this format for efficiency if you are executing DML statements
// within the for loop. Be careful not to exceed the 150 DML statement limit.
Account[] accts = new Account[];
for (List<Account> acct : [SELECT id, name FROM account
WHERE name LIKE 'Acme']) {
// Your logic here
accts.add(acct);
}
update accts;
Best Practice #7: Use of the Limits Apex Methods to Avoid Hitting Governor Limits
在处理大批量数据时我们可以通过下面的API了解我们是否超出了Limitation。
trigger accountLimitExample on Account (after delete, after insert, after update) {
System.debug('Total Number of SOQL Queries allowed in this Apex code context: ' + Limits.getLimitQueries());
System.debug('Total Number of records that can be queried in this Apex code context: ' + Limits.getLimitDmlRows());
System.debug('Total Number of DML statements allowed in this Apex code context: ' + Limits.getLimitDmlStatements() );
System.debug('Total Number of CPU usage time (in ms) allowed in this Apex code context: ' + Limits.getLimitCpuTime());
// Query the Opportunity object
List<Opportunity> opptys =
[select id, description, name, accountid, closedate, stagename from Opportunity where accountId IN: Trigger.newMap.keySet()];
System.debug('1. Number of Queries used in this Apex code so far: ' + Limits.getQueries());
System.debug('2. Number of rows queried in this Apex code so far: ' + Limits.getDmlRows());
System.debug('3. Number of DML statements used so far: ' + Limits.getDmlStatements());
System.debug('4. Amount of CPU time (in ms) used so far: ' + Limits.getCpuTime());
//NOTE:Proactively determine if there are too many Opportunities to update and avoid governor limits
if (opptys.size() + Limits.getDMLRows() > Limits.getLimitDMLRows()) {
System.debug('Need to stop processing to avoid hitting a governor limit. Too many related Opportunities to update in this trigger');
System.debug('Trying to update ' + opptys.size() + ' opportunities but governor limits will only allow ' + Limits.getLimitDMLRows());
for (Account a : Trigger.new) {
a.addError('You are attempting to update the addresses of too many accounts at once. Please try again with fewer accounts.');
}
}
else{
System.debug('Continue processing. Not going to hit DML governor limits');
System.debug('Going to update ' + opptys.size() + ' opportunities and governor limits will allow ' + Limits.getLimitDMLRows());
for(Account a : Trigger.new){
System.debug('Number of DML statements used so far: ' + Limits.getDmlStatements());
for(Opportunity o: opptys){
if (o.accountid == a.id)
o.description = 'testing';
}
}
update opptys;
System.debug('Final number of DML statements used so far: ' + Limits.getDmlStatements());
System.debug('Final heap size: ' + Limits.getHeapSize());
}
}
Best Practice #8: Use @future Appropriately
@future本身Salesforce就不推荐使用,所以如果必须使用还是要慎行。
Best Practice #9: Writing Test Methods to Verify Large Datasets
我们常常会忽略写一些Governor Limit的测试类。
public class sampleTestMethodCls {
static testMethod void testAccountTrigger(){
//First, prepare 200 contacts for the test data
Account acct = new Account(name='test account');
insert acct;
Contact[] contactsToCreate = new Contact[]{};
for(Integer x=0; x<200;x++){
Contact ct = new Contact(AccountId=acct.Id,lastname='test');
contactsToCreate.add(ct);
}
//Now insert data causing an contact trigger to fire.
Test.startTest();
insert contactsToCreate;
Test.stopTest();
}
}
Best Practices #10: Avoid Hardcoding IDs
//Query for the Account record types
List<RecordType> rtypes = [Select Name, Id From RecordType
where sObjectType='Account' and isActive=true];
//Create a map between the Record Type Name and Id for easy retrieval
Map<String,String> accountRecordTypes = new Map<String,String>{};
for(RecordType rt: rtypes)
accountRecordTypes.put(rt.Name,rt.Id);
for(Account a: Trigger.new){
//Use the Map collection to dynamically retrieve the Record Type Id
//Avoid hardcoding Ids in the Apex code
if(a.RecordTypeId==accountRecordTypes.get('Healthcare')){
//do some logic here.....
}else if(a.RecordTypeId==accountRecordTypes.get('High Tech')){
//do some logic here for a different record type...
}
}
这是使用Map可以减少我们代码量的写法:
public static void updateAccountWithCompetitor(List<Id> acctList){
List<Account> finalAccountList = new List<Account>();
List<Competitor__c> compForOpptyList = [SELECT Id, Opportunity__r.AccountId FROM Competitor__c WHERE Opportunity__c IN (SELECT Id FROM Opportunity WHERE AccountId IN: acctList )];
Map<Id, List<Competitor__c>> groupedData = new Map<Id, List<Competitor__c>>();
for(Competitor__c record: compForOpptyList ) {
if(groupedData.containsKey(record.Opportunity__r.AccountId)) {
groupedData.get(record.Opportunity__r.AccountId).add(record);
} else {
List<Competitor__c> tempList = new List<Competitor__c>();
tempList.add(record);
groupedData.put(record.Opportunity__r.AccountId, tempList);
}
}
for(Id accountId : acctList) {
finalAccountList.add(new Account(
Id = accountId,
PwC_Has_Competitors__c = groupedData.containsKey(accountId) && groupedData.get(accountId).size()>0
));
}
if(finalAccountList.size()>0){
update finalAccountList;
}
}
网友评论