Apex开发注意事项

Apex开发注意事项

*https://developer.salesforce.com/page/Apex_Code_Best_Practices

Best Practice #1: 在Trigger里成块儿的处理数据集

Bad Sample

trigger accountTestTrggr on Account (before insert, before update) {
   Account acct = Trigger.new[0];
   List<Contact> contacts = [select id, salutation, firstname, lastname, email 
              from Contact where accountId = :acct.Id];
   
}

Good Sample

trigger accountTestTrggr on Account (before insert, before update) {
   List<String> accountNames = new List<String>{};
   for(Account a: Trigger.new){
      a.Description = a.Name + ':' + a.BillingState
   }
}

知识点:Trigger的使用场景有两种
一种是画面单条记录更新,新建的时候会触发相应动作的Trigger,这个时候上边儿两个例子处理是一样的都没有问题。
另外一种情况是使用Dataloader活着其他的工作批量的导入数据时候也会触发相应的Tirgger,这时候Bad Sample的处理只能处理第一条(Trigger.new[0]),其他的数据就会丢失(准确的说是每200条,或者一个批处理的第一条),所以正确的做法是用处理数组的方法处理Trigger的逻辑。

Best Practice #2: 不要把SOQL语句写到循环里

Bad Sample

trigger accountTestTrggr on Account (before insert, before update) {
   for(Account a: Trigger.new) {
      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;  
         update c;
      }    	  
   }
}

Good Sample

trigger accountTestTrggr on Account (before insert, before update) {
  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(Account a: accountsWithContacts){
     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);
     }    	  
   }
   update contactsToUpdate;
}

知识点:Salesforce的每个事物的执行都是有限制的尤其是SOQL操作有着明确的规定,所以为了避免发生系统异常所以要把SOQL语句拿到循环外边儿,灵活使用List,Map,Set等数组对象,确保在select ,update,Insert等数据库操作语句里只执行一次。
Execution Governors and Limits
Limits Class

Best Practice #3:批量处理数据

确保HelperClass里的执行也是“批量处理数据”的原则来进行书写。
原因是Salesforce里的Limit是按照事物计算的不是按照文件来算的,所以无论是Apex还是Trigger,都应该尽量少的写操作SOQL语句。

Best Practice #4: 尽量给For循环操作提供集合

Bad Sample

trigger accountTrigger on Account (before delete, before insert, before update) {
    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){
    	
        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...');
        }
    }
}
}

Good Sample

trigger accountTrigger on Account (before delete, before insert, before update) {
 
    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){
    	 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...');
    	 	}
    	 }
    
   }
}

知识点:
好坏相比较来看:
1:SOQL语句少了一个肯定提高效率。
2:嵌套的For循环也减少了一个,改成了If判断的形式来实现了。
Salesforce之所以这么算计效率原因就是因为他是Online的产品,尽可能的减少资源浪费一直是他所推荐的。

Best Practice #5: 做好一个对象上多个Trigger 的排序

原则就是一个事物处理里不要有重复查询同一个数据集的操作。
一种办法是在一个大的Trigger里做好分支判断,保证每个业务的处理逻辑清晰。
另外一种办法是为多个业务做多个Trigger,从而保证Trigger处理数据不被干扰。

Best Practice #6: 使用For循环来查询大数据

Bad Sample

//A runtime exception is thrown if this query returns enough records to exceed your heap limit.
Account[] accts = [SELECT id FROM account];

Good Sample

// 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;

知识点:Salesforce的Limit里还有Heap的异常等等,所以在查询大数据量的同时为了避免出现系统异常建议采用For循环的形式来取得数据,并形成集合对象,最后在进行数据库操作。
因为For循环的处理是取200件处理一次,让后在处理200件,这样不会出现一次取数据过多导致系统异常的问题。

Best Practice #7: 灵活使用 Limits Class来判断和处理Limits的问题

事例代码:

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());
    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());
    }
}

DEBUG|Total Number of SOQL Queries allowed in this Apex code context: 100
DEBUG|Total Number of records that can be queried in this Apex code context: 10000
DEBUG|Total Number of DML statements allowed in this Apex code context: 150
DEBUG|Total Number of CPU usage time (in ms) allowed in this Apex code context: 10000
DEBUG|1. Number of Queries used in this Apex code so far: 1
DEBUG|2. Number of rows queried in this Apex code so far: 0
DEBUG|3. Number of DML statements used so far: 0
DEBUG|4. Amount of CPU time (in ms) used so far: 9
DEBUG|Continue processing. Not going to hit DML governor limits
DEBUG|Going to update 3 opportunities and governor limits will allow 10000
DEBUG|Number of DML statements used so far: 0
DEBUG|Final number of DML statements used so far: 1
DEBUG|Final heap size: 1819

Best Practice #8: 适时使用@future 功能

代码示例:

trigger accountAsyncTrigger on Account (after insert, after update) {
    //By passing the @future method a set of Ids, it only needs to be
    //invoked once to handle all of the data. 
    asyncApex.processAccount(Trigger.newMap.keySet());
}
global class asyncApex {
 
  @future 
  public static void processAccount(Set<Id> accountIds) {
       List<Contact> contacts = [select id, salutation, firstname, lastname, email from Contact where accountId IN :accountIds];
       for(Contact c: contacts){
 	 System.debug('Contact Id[' + c.Id + '], FirstName[' + c.firstname + '], LastName[' + c.lastname +']');
 	                        c.Description=c.salutation + ' ' + c.firstName + ' ' + c.lastname;
        }
        update contacts;
  }
}

知识点:@future 是异步调用的标记,带这个标记的方法被调用的时候不是马上执行,而是salesforce通过判断服务器端情况适时调用,这么做虽然不可预期执行时间,但是可以提交系统使用效率,避免触及salesforce的Limit。
注意点:如果异步方法和同步方法有可能同时更新同一条数据或者造成死锁的可能的话,需要引用锁的机制来解决。另外需要注意要批量处理数据。
异步方法调用也有Limit:
No more than 10 method calls per Apex invocation
No more than 200 method calls per Salesforce license per 24 hours
The parameters specified must be primitive dataypes, arrays of primitive datatypes, or collections of primitive datatypes.
Methods with the future annotation cannot take sObjects or objects as arguments.
Methods with the future annotation cannot be used in Visualforce controllers in either getMethodName or setMethodName methods, nor in the constructor.
It’s important to make sure that the asynchronous method

Best Practice #9: 如果有大量数据的处理逻辑,需要在测试Class也进行大数据量的测试

代码示例:

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();	
	}	
}

知识点:
1:在测试Class的创建测试数据阶段,适时的创建大于200件数据来测试多批次处理逻辑是否有问题
2:测试一个事物里的执行可以使用Test.startTest();和Test.stopTest();来包围测试对象方法,从而准确的看出结果。

Best Practices #10:停止在代码里使用Hard Code

Bad Sample

for(Account a: Trigger.new){
     	 
   //Error - hardcoded the record type id
   if(a.RecordTypeId=='012500000009WAr'){     	  	
      //do some logic here.....
   }else if(a.RecordTypeId=='0123000000095Km'){
      //do some logic here for a different record type...
   }
    	 
}

Good Sample

//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...
     	  }
     	 
     } 

知识点:Recordtype,Profile,user,queue,Group等都有单独的系统表存储数据,可以通过Select语句查询到,所以代码里需要这些数据的时候可以到系统表里查询,
原则上salesforce是不建议固定ID的,因为开发环境和生产环境的ID是不一样的,这样的Hardcode在开发环境上好用,在生产环境上是会出问题的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Chapter 1: Introducing Apex...............................................................................................................11 What is Apex?.........................................................................................................................................................................12 How Does Apex Work?..............................................................................................................................................13 What is the Apex Development Process?....................................................................................................................14 Using a Developer or Sandbox Organization...................................................................................................14 Learning Apex.................................................................................................................................................17 Writing Apex...................................................................................................................................................18 Writing Tests...................................................................................................................................................19 Deploying Apex to a Sandbox Organization...................................................................................................19 Deploying Apex to a Salesforce Production Organization...............................................................................20 Adding Apex Code to a Force.com AppExchange App..................................................................................20 When Should I Use Apex?..........................................................................................................................................20 What are the Limitations of Apex?......................................................................................................................

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值