salesforce對於數據操縱個數以及次數有嚴格的限制,超過限制值則拋出異常。
salesforce對於很多數據操縱的次數均有嚴格的限制。具體限制如下:
Number of SOQL queries: 100 -->一次執行SOQL的次數不能超過100次
Number of query rows: 50000 -->一次查出的數據行數不能超過50000條
Number of SOSL queries: 20 -->一次執行SOSL次數不能超過20次
Number of DML statements: 150 -->DML語句不能超過150條
Number of DML rows: 10000 -->一次操作數據行數不能超過10000行
Maximum CPU time: 10000 -->最大的CPU時間不能超過10000ms
Maximum heap size: 6000000 -->堆大小不能超過6000000B
Number of callouts:100 -->一次執行callouts次數不能超過100次
Number of Email Invocations: 10 -->Email調用次數不能超過10次
Number of future calls: 50 -->調用Future次數不能超過50次
Number of queueable jobs added to the queue:50 -->添加到隊列的queueable job數量不能超過50次
Number of Mobile Apex push calls: 10 -->移動端Apex push調用最多不能超過10次
因為對於DML操作有限制,比如因為項目需求,需要修改50萬條數據,直接調用Database.update()便會拋出異常,因為salesforce只允許一次性查出5萬條數據並且只允許一次性修改1萬條數據。如果需要達到目的,就只能使用批處理。
一)數據批處理Batchable
數據批處理適用於批量處理成百上千萬的數據。批處理采用異步的處理方式處理數據,最多可以處理5000萬條數據。新建一個批處理類需要實現Database.Batchable接口。此接口封裝了三個方法,並且三個方法構成一個批處理的生命周期。start()方法用於查詢數據,並將查詢數據封裝到List中;execute()方法用於操作數據,形參中List為start()方法中返回的數據,可以直接對此List進行修改以達到批處理行為。批處理全部執行后執行finish()方法,finish()方法用於進行一些后期處理,比如發郵件等操作。
需要注意的是:
1.start()方法執行后,數據便無法修改;
2.execute()原則上可以執行多次,比如在調用的時規定執行次數,則按照規定次數執行execute();
3.finish()方法執行以后,批處理類用到的所有的變量對象都會恢復到最開始的狀態,即值回滾到最開始狀態;
4.如果批處理類不實現Database.Stateful接口,則變量只在相應方法起作用,當方法執行完成,變量則會回滾到初始狀態。
eg:在類中聲明成員變量A,在start()方法對A進行處理,如果類不實現上述接口,則方法執行完start()方法后A會回滾到初始狀態,在execute()方法或者finish()方法調用A時值為最開始聲明的值,在start方法的處理結果不保留。
實現批處理類步驟明確,只需要執行以下的步驟:
1.實現Database.Batchable接口;
2.實現start()方法,此方法中通常寫查詢語句,並將數據通過Database.getQueryLocator(queryString)方法將數據傳遞到execute()形參中。此方法定義:
global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {} ;
3.實現execute()方法,此方法對數據進行DML操作。此方法定義:global void execute(Database.BatchableContext BC, list<P>){} ;
4.實現finish方法(),此方法進行后期處理,如果無需要處理,可以不進行處理。
上面步驟提到了Database.BatchableContext接口,此接口用於追蹤批處理的進展。通過此接口可以獲取相關的jobId,詳情請參看官方文檔。
下面舉個例子,創建一個商品表GOODS__c,里面含有一個字段為價格GoodsPrice__c。現在需要將原來數據的GoodsPrice__c加1,代碼如下:

1 global with sharing class GoodsBatch implements Database.Batchable<sObject>,Database.Stateful{ 2 Integer queryCount = 0; 3 4 String myEmailAddress = 'myAddress@xx.com'; 5 6 global Database.QueryLocator start(database.BatchableContext bc ) 7 { 8 String query = 'select GOODSPRICE__c,Id from GOODS__c'; 9 return Database.getQueryLocator(query); 10 } 11 12 global void execute (Database.BatchableContext bc, List<GOODS__c> goodsList) 13 { 14 for(GOODS__c goods : goodsList) { 15 Decimal price = goods.GoodsPrice__c; 16 price += 1; 17 queryCount +=1; 18 } 19 upsert goodsList; 20 } 21 22 global void finish(Database.BatchableContext bc) 23 { 24 /*--------execute finish----------*/ 25 /*注意:如果不實現Database.Stateful接口,則queryCount為0 26 因為在execute執行完成便會回滾到初始狀態*/ 27 System.debug('query count:' + queryCount); 28 //send email 29 Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage(); 30 email.setToAddresses(new String[]{myEmailAddress});//set mail getter 31 email.setSubject('show count'); //set subject 32 email.setHtmlBody('query Count' + queryCount); 33 Messaging.sendEmail(new Messaging.SingleEmailMessage[] { email }); 34 } 35 } 36 37 implements Batchable
二)異步進程簡單介紹
異步進程用於在單獨的線程內來運行進程。異步進程是一個在后台運行,不需要用戶等到任務結束的進程或者方法。異步進程好處很多,包括不需要用戶等待,節省響應時間等等。
異步進程主要有以下幾種形式:
類型 | 介紹 | 常用情景 |
Future方法 | 在自己線程中運行,直到資源可用才運行 | Web service callout. |
Batch Apex | 運行大量的Job,數量超過正常處理限制 | 數據DML操作 |
QueueableApex | 和Future類似,但是提供額外的工作鏈,允許完成更復雜的類型 | 執行順序處理操作與外部Web服務。 |
ScheduledApex | 指定時間運行apex | 固定時間的任務,例如每日或每周等任務 |
- Future方法
Future方法用於異步處理,常用於Web service callout操作.Future方法需要有幾個規范:
1.方法必須是靜態static的;
2.方法上方需要使用@Future標簽;
3.方法返回類型必須是void類型;
4.方法參數必須是模塊私有的變量,不能使public等;
5.方法參數不允許使用標准的Object或sObject類型,可以使用基本類型或者集合類型;
6.不能再一個future方法調用另一個future方法,當future方法運行的時候也不可以在trigger中調用;
7.future方法中不能使用getContent()和getContentAsPDF()方法。
以下為Future方法代碼舉例。此方法用來輸出符合Id在形參List中的所有Account的Id。
public with sharing class FutureSample { @future public static void futuremethod(List<ID> ids) { String sql = 'select Id,Name from Account where Id in :ids'; List<Account> accounts = Database.query(sql); for(Account account : accounts) { System.debug(account.Id); } } }
有幾點需要注意:
1)future方法執行不保證質量,如果需要好的質量可以使用Queueable方法;
2)可以允許兩個future方法同時運行,當兩個future方法同時對一條記錄進行操作時,可能引起記錄鎖定或者運行時異常。
總之,使用future方式不保證質量。。。。。。而且有很多限制,開發的時候能不用就不用,如果必須使用情況下自己評估一下。
測試future方法在Test類中執行,和普通的方法測試區別的是,future方法執行需要在Test.startTest()和Test.stopTest()方法中進行.以下為測試代碼:
@isTest private class Test_FutureSample { static testMethod void myUnitTest() { Test.startTest(); List<ID> ids= new ID[]{'0012800000Hz6ozAAB','0012800000Hz6oxAAB'}; FutureSample.futuremethod(ids); Test.stopTest(); } }
- Queueable
Queueable接口有着類似future的特性,類似將future特性和批處理功能混合在一起,相對future方法來講,有很大的優勢:
1.可以使用Object和sObject類型作為參數;
2.便於監控,可以直接通過System.enqueueJob()方法運行返回AsyncApexJob ,方法不用限制在startTest()和stopTest()方法中;
3.可以鏈接兩個job,一個Queueable接口方法可以調用另一個Queueable接口。
Queueable在執行異步的時候大部分可以替代掉future,但是不是所有的情況都可以替換。當一個方法有時需要同步執行有時需要異步執行,相對來講用future操作更為簡單,畢竟不需要修改方法的內容,只是注解而已。
Queueable接口代碼舉例:
public with sharing class QueueableSample implements Queueable{ private List<ID> ids{get;set;} public QueueableSample(List<ID> ids) { this.ids = ids; } public void execute(QueueableContext qc) { String sql = 'select Id,Name from Account where Id in :ids'; List<Account> accounts = Database.query(sql); for(Account account : accounts) { System.debug(account.Id); } } }
運行實現QueueableSample接口的類的方式如下:
@isTest private class Test_QueueableSample { static testMethod void myUnitTest() { Test.startTest(); List<ID> ids= new ID[]{'0012800000Hz6ozAAB','0012800000Hz6oxAAB'}; QueueableSample sample = new QueueableSample(ids); ID jobID = System.enqueueJob(sample); Test.stopTest(); } }
Queueable盡管很好用很強大,不過force.com對於Queueable有很多限制和規范,詳情請參看官方文檔。
- ScheduledApex
定時任務相對來說,使用比較方便。當你需要在指定時間日期去執行某些操作(比如定期清理垃圾數據等等)時,定時任務就顯得尤為便利。
定時任務的聲明和調用都很簡單,通過以下步驟即可完成操作:
1.實現Schedulable接口,並重寫execute方法,此方法體內實現需要定時執行的操作;
2.使用System.schedule()方法實現定時任務的調用。
Schedulable接口代碼舉例如下:
public class GoodsSchedule implements Schedulable { public void execute(SchedulableContext sc) { String queryString = 'select Id,GOODSNAME__c from GOODS__c'; SimpleBatchUtil batchUtil = new SimpleBatchUtil(queryString); Database.executeBatch(batchUtil); } }
上述代碼定義了一個定時任務,定時任務的方法體內實現批處理操作GOODS表
Schedulable接口調用如下所示:
@isTest private class TestGoodsSchedule { static testMethod void myUnitTest() { String executeTime = '0 10 2 * * ?'; GoodsSchedule goodsSchedule = new GoodsSchedule(); System.schedule('batch goods',executeTime,goodsSchedule); } }
注意:定時任務在每24小時同時只允許最多100個定時任務。超過數量則會拋出異常。
System.schedule()方法有三個參數:第一個參數為定時任務名稱;第二個參數為定時任務執行時間;第三個參數為需要執行的定時任務的對象。
關於定時任務執行時間有很多需要注意的地方:
執行時間字符串通過空格分隔每個時間點,時間點的順序為:
Seconds Minutes Hours Day_of_month Month Day_of_week optional_year
每個時間點的取值如下所示:
名稱 | 取值范圍 | 特殊字符 |
Seconds | 0-59 | NONE |
Minutes | 0-59 | NONE |
Hours | 23 | , - * / |
Day_of_month | 1--31 | , - * / ? L W |
Month | 1--12或者JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC |
, - * / |
Day_of_week | 1--7或者SUN,MON,TUE,WED,THU,FRI,SAT | , - * / ? L # |
optional_year | null或者1970--2099 | , - * / |
特殊字符包含八種,下面是對他們的解釋:
特殊字符名稱 | 特殊字符解釋 |
, | 定界值。比如Hours設置1,2則只有小時為1或者2的時候執行 |
- | 指定一個范圍。比如Day_of_week設置2-6則周一到周五執行 |
* | 指定所有值。比如Day_of_month指定*則每天都執行 |
? | 沒有指定特定的值,只在Day_of_month和Day_of_week中執行 |
/ | /左側指定間隔什么時間開始,/右側顯示間隔數量。eg:對於Day_of_month,指定1/5則每個月的每個第五天開始運行第一天 |
L | 只應用於Day_of_month以及Day_of_week.用於Day_of_month代表當月最后一天,用於Day_of_week,代表每月最后一個周幾. eg : 1L代表每月最后一個周日 |
W | 只應用於Day_of_month.指定最接近與當天的工作日,比如指定20W,20為周六,則值為19,即星期五。如果指定1W,1為周六,最接近的為上個月,則不可取,取第三日,即周一。 |
# | 只應用於Day_of_week.格式為weekday#day_of_month。其中,#以前代表工作日1-7(周日-周六),#以后指定月的第幾天。eg:2#2代表每個月第二個周一執行。 |
通過幾個例子舉例:
0 0 13 * * ? 指定每天13點執行
0 0 22 ? * 6L 指定每個月最后一個周五22點執行
0 0 10 ? * MON-FRI 指定周一到周五10點執行
0 0 20 * * ? 2016 2016年每天20點執行
Schedulable除了在代碼中通過System.schedule()方法啟動定時任務還可以通過頁面設置啟動定時器。步驟如下:
1.點擊setup-->develop-->Apex Classes;
2.點擊Schedule Apex按鈕;
3.輸入Job Name,為定時任務顯示的任務名稱,點擊Apex Class的查找按鈕選擇需要定時任務的實現Schedulable接口的類,設定時間,點擊保存;
4.定時任務創建成功,在setup-->Jobs-->Scheduled Jobs中可以看到創建的定時任務了。
通過頁面設置啟動定時器和代碼的區別為:使用頁面配置定時器無法精確到分和秒。
由於本人對於Salesforce也是一個小白,所以如果有的內容有錯誤,歡迎批評指正。如果有不懂的問題,可以留言,大家共同探討。下一篇將描述簡單的數據增刪改查頁面的構建。