salesforce 零基础开发入门学习(五)异步进程介绍与数据批处理Batchable

本篇知识参考:https://developer.salesforce.com/trailhead/force_com_dev_intermediate/asynchronous_apex/async_apex_batchjava

salesforce对于数据操纵个数以及次数有严格的限制,超过限制值则抛出异常。sql

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次async

由于对于DML操做有限制,好比由于项目需求,须要修改50万条数据,直接调用Database.update()便会抛出异常,由于salesforce只容许一次性查出5万条数据而且只容许一次性修改1万条数据。若是须要达到目的,就只能使用批处理。ide

一)数据批处理Batchable测试

数据批处理适用于批量处理成百上千万的数据。批处理采用异步的处理方式处理数据,最多能够处理5000万条数据。新建一个批处理类须要实现Database.Batchable接口。此接口封装了三个方法,而且三个方法构成一个批处理的生命周期。start()方法用于查询数据,并将查询数据封装到List中;execute()方法用于操做数据,形参中List为start()方法中返回的数据,能够直接对此List进行修改以达到批处理行为。批处理所有执行后执行finish()方法,finish()方法用于进行一些后期处理,好比发邮件等操做。this

须要注意的是:spa

1.start()方法执行后,数据便没法修改;线程

2.execute()原则上能够执行屡次,好比在调用的时规定执行次数,则按照规定次数执行execute();debug

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也是一个小白,因此若是有的内容有错误,欢迎批评指正。若是有不懂的问题,能够留言,你们共同探讨。下一篇将描述简单的数据增删改查页面的构建。

相关文章
相关标签/搜索