salesforce 零基礎開發入門學習(五)異步進程介紹與數據批處理Batchable


本篇知識參考:https://developer.salesforce.com/trailhead/force_com_dev_intermediate/asynchronous_apex/async_apex_batch

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
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也是一個小白,所以如果有的內容有錯誤,歡迎批評指正。如果有不懂的問題,可以留言,大家共同探討。下一篇將描述簡單的數據增刪改查頁面的構建。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM