Apex 提供了多种异步运行 Apex 代码的方法。选择 最适合您需求的异步 Apex 功能。
下表列出了异步 Apex 功能以及何时使用每个功能。
异步 Apex 功能 | 何时使用 |
---|---|
可排队的Apex | 启动长时间运行的操作并获取其 ID将复杂类型传递给作业链接作业 |
预定Apex | 计划 Apex 类按特定计划运行 |
批处理Apex | 对于需要批量执行的具有大量数据的长时间运行的作业,例如 作为数据库维护作业对于需要比常规事务允许的更大的查询结果的作业 |
未来方法 | 当您有一个长时间运行的方法并且需要防止延迟 Apex 时 交易对外部 Web 服务进行标注时隔离 DML 操作并绕过混合保存 DML 错误 |
- 可排队的 Apex 使用界面控制异步 Apex
进程。通过此接口,您可以将作业添加到队列中并对其进行监视。与使用未来方法相比,使用该接口是运行异步 Apex 代码的增强方式。Queueable - Apex 调度程序
- 批处理Apex
- 未来方法
可排队的Apex
使用该接口控制异步 Apex 进程。通过此接口,您可以添加 作业到队列中并对其进行监视。使用该界面是运行 异步 Apex 代码与使用未来方法的比较。
Queueable
长时间运行的 Apex 进程,例如大量数据库操作或 外部 Web 服务标注,可以通过实现接口并向 Apex 添加作业来异步运行 作业队列。这样,您的异步 Apex 作业就会在后台自行运行 线程,并且不会延迟主 Apex 逻辑的执行。每个排队的作业都运行 当系统资源可用时。使用接口方法的一个好处是一些 调控器限制高于同步 Apex 的限制,例如堆大小限制。QueueableQueueable
注意
如果 Apex 事务回滚,则任何排队等待执行的可排队作业 交易不被处理。
可排队作业与将来的方法类似,因为它们都排队等待 执行,但它们为您提供了这些额外的好处。
- 获取作业的 ID:通过调用该方法提交作业时,该方法将返回 新作业的 ID。此 ID 对应于 AsyncApexJob 记录的 ID。 使用此 ID 通过 Salesforce UI 识别和监控您的作业 (Apex Jobs 页面),或以编程方式查询您的记录 异步Apex作业。System.enqueueJob
- 使用非基元类型:可排队类可以包含 非原始数据类型,例如 sObject 或自定义 Apex 类型。那些对象 可以在作业执行时访问。
- 链接作业:您可以通过启动第二个作业将一个作业链接到另一个作业 从正在运行的作业。如果您的进程依赖于另一个进程,则链接作业很有用 进程首先运行。
您可以设置链接的可排队作业的最大堆栈深度,覆盖默认值 开发人员和试用版组织中的限制为 5 个。
注意
声明的变量包括 被序列化和反序列化忽略,并且该值在 可排队的Apex。transient
将可排队作业添加到异步执行队列
此示例实现接口。此示例中的方法插入一个新帐户。该方法用于 将作业添加到 队列。
QueueableexecuteSystem.enqueueJob(queueable)
public class AsyncExecutionExample implements Queueable {
public void execute(QueueableContext context) {
Account a = new Account(Name='Acme',Phone='(415) 555-1212');
insert a;
}
}
若要将此类添加为队列中的作业,请调用 方法:
ID jobID = System.enqueueJob(new AsyncExecutionExample());
提交可排队类以供执行后,作业将添加到队列中 并将在系统资源可用时进行处理。您可以监控 通过查询 AsyncApexJob 或通过用户以编程方式获取作业的状态 通过输入“快速查找”框,然后选择“Apex”,在“设置”中界面 工作。Apex Jobs
若要查询有关已提交作业的信息,请在 AsyncApexJob 上执行 SOQL 查询 通过筛选方法返回的作业 ID。此示例使用 jobID 变量 这是在前面的示例中获得的。System.enqueueJob
AsyncApexJob jobInfo = [SELECT Status,NumberOfErrors FROM AsyncApexJob WHERE Id=:jobID];
与将来的作业类似,可排队的作业不处理批处理,因此 已处理的批次和总批次数始终为零。
添加具有指定最小延迟的可排队作业
使用该方法将可排队作业添加到具有指定 最小延迟(0-10 分钟)。在 Apex 测试期间,延迟将被忽略。System.enqueueJob(queueable, delay)
请参阅 Apex 中的 System.enqueueJob(queueable, delay) 参考指南。
警告
将延迟设置为 0(零)时,可排队作业将作为 越快越好。使用链接的可排队作业,实现一种减慢速度的机制 或在必要时停止作业。如果没有这样的故障安全机制,您可以 快速达到每日异步 Apex 限制。
在以下情况下,在 运行可排队作业。
- 如果外部系统是有速率限制的,并且可以通过链接过载 可排队的作业,这些作业正在进行快速标注。
- 轮询结果时,如果执行速度过快可能会导致浪费 每日异步 Apex 限制。
此示例通过传入实例来添加延迟异步执行的作业 用于执行的接口的类实现。在作业开始前,至少需要 5 分钟的延迟 执行。Queueable
Integer delayInMinutes = 5;
ID jobID = System.enqueueJob(new MyQueueableClass(), delayInMinutes);
管理员可以在计划中定义默认的组织范围延迟(1-600 秒) 在没有延迟参数的情况下计划的可排队作业。使用延迟设置 作为减慢默认可排队作业执行速度的机制。如果省略该设置, Apex 使用标准的可排队计时,不会增加延迟。
注意
使用该方法将忽略任何组织范围的排队延迟设置。System.enqueueJob(queueable, delay)
通过以下方式之一定义组织范围的延迟。
- 在“设置”的“快速查找”框中,输入 、 ,然后为默认最小值输入一个值(1-600 秒) 没有延迟的可排队作业的排队延迟(以秒为单位) 参数Apex Settings
- 若要使用元数据 API 以编程方式启用此功能,请参阅元数据 API 中的 ApexSettings 开发人员指南。
添加具有指定堆栈深度的可排队作业
使用可在 asyncOptions 参数。System.enqueueJob(queueable, asyncOptions)
System.AsyncInfo 类属性 包含当前和最大堆栈深度以及最小可排队延迟。
System.AsyncInfo 类具有 帮助您确定是否在 Queueable 中设置了最大堆栈深度的方法 请求,并获取可排队对象的堆栈深度和队列延迟 当前正在运行。使用有关当前可排队执行的信息使 关于调整后续通话延迟的决定。
这些是类中的方法。System.AsyncInfo
- hasMaxStackDepth()
- getCurrentQueueableStackDepth()
- getMaximumQueueableStackDepth()
- getMinimumQueueableDelayInMinutes()
This example uses stack depth to terminate a chained job and prevent it from reaching the daily maximum number of asynchronous Apex method executions.
// Fibonacci
public class FibonacciDepthQueueable implements Queueable {
private long nMinus1, nMinus2;
public static void calculateFibonacciTo(integer depth) {
AsyncOptions asyncOptions = new AsyncOptions();
asyncOptions.MaximumQueueableStackDepth = depth;
System.enqueueJob(new FibonacciDepthQueueable(null, null), asyncOptions);
}
private FibonacciDepthQueueable(long nMinus1param, long nMinus2param) {
nMinus1 = nMinus1param;
nMinus2 = nMinus2param;
}
public void execute(QueueableContext context) {
integer depth = AsyncInfo.getCurrentQueueableStackDepth();
// Calculate step
long fibonacciSequenceStep;
switch on (depth) {
when 1, 2 {
fibonacciSequenceStep = 1;
}
when else {
fibonacciSequenceStep = nMinus1 + nMinus2;
}
}
System.debug('depth: ' + depth + ' fibonacciSequenceStep: ' + fibonacciSequenceStep);
if(System.AsyncInfo.hasMaxStackDepth() &&
AsyncInfo.getCurrentQueueableStackDepth() >=
AsyncInfo.getMaximumQueueableStackDepth()) {
// Reached maximum stack depth
Fibonacci__c result = new Fibonacci__c(
Depth__c = depth,
Result = fibonacciSequenceStep
);
insert result;
} else {
System.enqueueJob(new FibonacciDepthQueueable(fibonacciSequenceStep, nMinus1));
}
}
}
测试可排队作业
此示例演示如何在测试方法中测试可排队作业的执行。一个 可排队作业是一个异步进程。确保此过程在 测试方法中,作业被提交到 AND 块之间的队列中。系统执行所有 异步进程在语句之后同步启动测试方法。接下来,测试方法 通过查询作业所在的帐户来验证可排队作业的结果 创建。
Test.startTestTest.stopTestTest.stopTest
@isTest
public class AsyncExecutionExampleTest {
@isTest
static void test1() {
// startTest/stopTest block to force async processes
// to run in the test.
Test.startTest();
System.enqueueJob(new AsyncExecutionExample());
Test.stopTest();
// Validate that the job has run
// by verifying that the record was created.
// This query returns only the account created in test context by the
// Queueable class method.
Account acct = [SELECT Name,Phone FROM Account WHERE Name='Acme' LIMIT 1];
System.assertNotEquals(null, acct);
System.assertEquals('(415) 555-1212', acct.Phone);
}
}
链接作业
若要在另一个作业首先完成其他处理后运行作业,可以链接 可排队的作业。若要将一个作业链接到另一个作业,请从可排队的方法提交第二个作业 类。您只能从正在执行的作业中添加一个作业,这意味着只能添加一个作业 每个父作业都可以存在子作业。例如,如果您有第二个班级 调用来实现接口,您可以将此类添加到 方法中的队列为 遵循:execute()SecondJobQueueableexecute()
public class AsyncExecutionExample implements Queueable {
public void execute(QueueableContext context) {
// Your processing logic here
// Chain this job to next job by submitting the next job
System.enqueueJob(new SecondJob());
}
}
注意
Apex 允许来自可排队作业的 HTTP 和 Web 服务标注(如果它们实现) 标记 接口。在实现此接口的可排队作业中,标注也是 允许在链接的可排队作业中。Database.AllowsCallouts
您可以使用适当的堆栈深度测试链接的可排队作业,但请注意 适用的 Apex 调速器限制。请参阅添加具有指定堆栈深度的可排队作业。
可排队的Apex限制
- 排队作业的执行计入共享限制一次 异步 Apex 方法执行。请参阅 Lightning 平台Apex限制。
- 在单个事务中,您最多可以将 50 个作业添加到队列中。异步 事务(例如,从批处理 Apex 作业),您只能将一个作业添加到 带有 的队列。自 检查一个事务中添加了多少个可排队的作业,调用 Limits.getQueueableJobs()。System.enqueueJobSystem.enqueueJob
- 由于对链接作业的深度没有限制,因此可以链接一个作业 到另一个。您可以对每个新的子作业重复此过程,以将其链接到 新的子工作。对于 Developer Edition 和 Trial 组织,最大堆栈 链式作业的深度为 5,这意味着您可以链式作业四次。这 链中的最大作业数为 5,包括初始父级可排队作业 工作。
- 将作业与 链接时,只能从正在执行的作业中添加一个作业。 每个父可排队作业只能存在一个子作业。启动多个 不支持来自同一可排队作业的子作业。System.enqueueJob
- 检测重复的可排队作业 通过基于签名仅对异步可排队作业
的单个实例进行排队,减少资源争用和争用条件。尝试将多个 Queueable 作业添加到具有相同签名的处理队列中会导致 DuplicateMessageException,当您尝试将后续作业加入队列时。 - 事务终结器
通过事务终结器功能,您可以使用接口将操作附加到使用 Queueable 框架的异步 Apex 作业。一个特定的用例是在可排队作业失败时设计恢复操作。System.Finalizer - 事务终结器错误消息 通过分析这些错误消息
来解决语义和运行时问题。
检测重复的可排队作业
通过仅将 基于签名的异步可排队作业的单个实例。尝试添加更多内容 将一个 Queueable 作业发送到具有相同签名的处理队列会导致 DuplicateMessageException,当您尝试将后续作业排入队列时。
实施细节
使用 类。 使用 中的这些方法添加不同的字符串、ID 或整数。QueueableDuplicateSignature.BuilderQueueableDuplicateSignature.Builder
- addString(inputString)
- addId(inputId)
- addInteger(inputInteger)
当签名具有所需的组件时,调用该方法并存储唯一的可排队作业 在属性中的签名 类。使用将作业排入队列 带有参数的方法。.build()DuplicateSignatureAsyncOptionsSystem.enqueueJob()AsyncOptions
要确定大小, 剩余大小和可排队作业签名的最大大小(以字节为单位)使用以下命令 类中的方法。QueueableDuplicateSignature.Builder
- getSize()
- getRemainingSize()
- getMaxSize()
例子
此示例使用 UserId 和字符串 .MyQueueable
AsyncOptions options = new AsyncOptions();
options.DuplicateSignature = QueueableDuplicateSignature.Builder()
.addId(UserInfo.getUserId())
.addString('MyQueueable')
.build();
try {
System.enqueueJob(new MyQueueable(), options);
} catch (DuplicateMessageException ex) {
//Exception is thrown if there is already an enqueued job with the same
//signature
Assert.areEqual('Attempt to enqueue job with duplicate queueable signature',
ex.getMessage());
}
此示例使用 ApexClass Id 和 一个 sObject。
AsyncOptions options = new AsyncOptions();
options.DuplicateSignature = QueueableDuplicateSignature.Builder()
.addInteger(System.hashCode(someAccount))
.addId([SELECT Id FROM ApexClass
WHERE Name='MyQueueable'].Id)
.build();
System.enqueueJob(new MyQueueable(), options);
交易终结器
使用事务终结器功能可以附加操作、 使用接口,以 使用 Queueable 框架的异步 Apex 作业。一个具体的用例是设计 可排队作业失败时的恢复操作。
System.Finalizer“事务终结器”功能为您提供了直接指定 异步作业成功或失败时要执行的操作。交易前 终结器,对于异步作业失败,只能执行以下两项操作:
- 轮询使用 SOQL 查询的状态,如果作业失败,则重新排队AsyncApexJob
- 当批处理 Apex 方法遇到未处理的 例外
使用事务终结器,可以将操作后序列附加到 Queueable job,并根据作业执行结果采取相关操作。
一个可排队的作业,该作业 由于未处理的异常而失败,可以通过 事务终结器。此限制适用于一系列连续的可排队作业 失败。当 Queueable 作业完成而没有未处理时,计数器将重置 例外。
终结器可以作为内部类实现。此外,您还可以 使用相同的类实现 Queueable 和 Finalizer 接口。这 可排队作业和终结器在单独的 Apex 和数据库事务中运行。为 例如,Queueable 可以包含 DML,终结器可以包含 REST 标注。 使用终结器不算作针对每日异步 Apex 的额外执行 限制。同步调控器限制适用于终结器事务,但 异步限制适用的以下情况:
- 总堆大小
- 添加到队列的最大 Apex 作业数System.enqueueJob
- 每个 Apex 调用允许的具有注释的方法的最大数量future
有关调控器限制的详细信息,请参阅执行调控器和限制。
System.Finalizer 接口
该接口包括以下方法:
System.Finalizerexecute
global void execute(System.FinalizerContext ctx) {}
这 方法在为每个排队作业提供的 FinalizerContext 实例上调用 附有终结器。在该方法中,您可以定义在 可排队作业。Apex 运行时引擎将 的实例作为 参数添加到 execute 方法。
executeSystem.FinalizerContext
System.FinalizerContext 接口
界面 包含四种方法。
System.FinalizerContext
- getAsyncApexJobId方法:
global Id getAsyncApexJobId {}
返回 为其定义此终结器的可排队作业的 ID。 - getRequestId方法:
global String getRequestId {}
返回 请求 ID,唯一标识请求的字符串,可以是 与事件监控日志相关联。与 AsyncApexJob 关联 表中,请改用该方法。Queueable 作业和终结器执行都共享 (相同)请求 ID。getAsyncApexJobId - getResult方法:
global System.ParentJobResult getResult {}
返回 枚举, 表示父异步 Apex Queueable 作业的结果 附上终结器。枚举采用以下值:、。System.ParentJobResultSUCCESSUNHANDLED_EXCEPTION - getException方法:
global System.Exception getException {}
返回 Queueable 作业在以下情况下失败的异常为 , null 否则。getResultUNHANDLED_EXCEPTION
使用该方法将终结器附加到可排队作业。
System.attachFinalizer
- 定义实现接口的类。System.Finalizer
- 在 Queueable 作业的方法中附加终结器。若要附加终结器,请使用 As 参数:实现 System.Finalizer 的实例化类 接口。executeSystem.attachFinalizer
global void attachFinalizer(Finalizer finalizer) {}
实施细节
- 只能将一个终结器实例附加到任何可排队作业。
- 您可以将单个异步 Apex 作业(Queueable、Future 或 Batch)加入队列 在终结器的方法实现中。execute
- 终结器实现中允许使用标注。
- 终结器框架使用终结器对象的状态(如果附加) 在可排队执行结束时。终结器状态的突变,在 它是附加的,因此是受支持的。
- 声明的变量包括 被序列化和反序列化忽略,因此不会保留在 事务终结器。transient
日志记录终结器示例
此示例演示如何使用事务终结器来记录来自 a 可排队作业,无论作业是成功还是失败。这 此处的 LoggingFinalizer 类实现了 Queueable 和 Finalizer 接口。这 可排队实现实例化终结器,附加它,然后调用 addLog() 方法来缓冲日志消息。终结器实现 LoggingFinalizer 包括允许缓冲的 addLog(message, source) 方法 将 Queueable 作业中的消息记录到终结器的状态。当 Queueable 作业 完成后,终结器实例将提交缓冲的日志。终结器状态为 即使 Queueable 作业失败,也可以保留,并且可以在 DML 中使用 终结器实现或执行。
public class LoggingFinalizer implements Finalizer, Queueable {
// Queueable implementation
// A queueable job that uses LoggingFinalizer to buffer the log
// and commit upon exit, even if the queueable execution fails
public void execute(QueueableContext ctx) {
String jobId = '' + ctx.getJobId();
System.debug('Begin: executing queueable job: ' + jobId);
try {
// Create an instance of LoggingFinalizer and attach it
LoggingFinalizer f = new LoggingFinalizer();
System.attachFinalizer(f);
// While executing the job, log using LoggingFinalizer.addLog()
// Note that addlog() modifies the Finalizer's state after it is attached
DateTime start = DateTime.now();
f.addLog('About to do some work...', jobId);
while (true) {
// Results in limit error
}
} catch (Exception e) {
System.debug('Error executing the job [' + jobId + ']: ' + e.getMessage());
} finally {
System.debug('Completed: execution of queueable job: ' + jobId);
}
}
// Finalizer implementation
// Logging finalizer provides a public method addLog(message,source) that allows buffering log lines from the Queueable job.
// When the Queueable job completes, regardless of success or failure, the LoggingFinalizer instance commits this buffered log.
// Custom object LogMessage__c has four custom fields-see addLog() method.
// internal log buffer
private List<LogMessage__c> logRecords = new List<LogMessage__c>();
public void execute(FinalizerContext ctx) {
String parentJobId = '' + ctx.getAsyncApexJobId();
System.debug('Begin: executing finalizer attached to queueable job: ' + parentJobId);
// Update the log records with the parent queueable job id
System.Debug('Updating job id on ' + logRecords.size() + ' log records');
for (LogMessage__c log : logRecords) {
log.Request__c = parentJobId; // or could be ctx.getRequestId()
}
// Commit the buffer
System.Debug('committing log records to database');
Database.insert(logRecords, false);
if (ctx.getResult() == ParentJobResult.SUCCESS) {
System.debug('Parent queueable job [' + parentJobId + '] completed successfully.');
} else {
System.debug('Parent queueable job [' + parentJobId + '] failed due to unhandled exception: ' + ctx.getException().getMessage());
System.debug('Enqueueing another instance of the queueable...');
}
System.debug('Completed: execution of finalizer attached to queueable job: ' + parentJobId);
}
public void addLog(String message, String source) {
// append the log message to the buffer
logRecords.add(new LogMessage__c(
DateTime__c = DateTime.now(),
Message__c = message,
Request__c = 'setbeforecommit',
Source__c = source
));
}
}
重试可排队示例
此示例演示如何在终结器中将失败的 Queueable 作业重新排队。 它还显示作业可以重新排队,最多可排队链接限制为 5 重试。
public class RetryLimitDemo implements Finalizer, Queueable {
// Queueable implementation
public void execute(QueueableContext ctx) {
String jobId = '' + ctx.getJobId();
System.debug('Begin: executing queueable job: ' + jobId);
try {
Finalizer finalizer = new RetryLimitDemo();
System.attachFinalizer(finalizer);
System.debug('Attached finalizer');
Integer accountNumber = 1;
while (true) { // results in limit error
Account a = new Account();
a.Name = 'Account-Number-' + accountNumber;
insert a;
accountNumber++;
}
} catch (Exception e) {
System.debug('Error executing the job [' + jobId + ']: ' + e.getMessage());
} finally {
System.debug('Completed: execution of queueable job: ' + jobId);
}
}
// Finalizer implementation
public void execute(FinalizerContext ctx) {
String parentJobId = '' + ctx.getAsyncApexJobId();
System.debug('Begin: executing finalizer attached to queueable job: ' + parentJobId);
if (ctx.getResult() == ParentJobResult.SUCCESS) {
System.debug('Parent queueable job [' + parentJobId + '] completed successfully.');
} else {
System.debug('Parent queueable job [' + parentJobId + '] failed due to unhandled exception: ' + ctx.getException().getMessage());
System.debug('Enqueueing another instance of the queueable...');
String newJobId = '' + System.enqueueJob(new RetryLimitDemo()); // This call fails after 5 times when it hits the chaining limit
System.debug('Enqueued new job: ' + newJobId);
}
System.debug('Completed: execution of finalizer attached to queueable job: ' + parentJobId);
}
}
最佳实践
我们敦促 ISV 在使用具有状态突变的全局终结器时要谨慎 包中的方法。如果订阅者组织的实现在 全局终结器,可能会导致意外行为。检查所有 状态突变方法,用于查看它们如何影响终结器状态和整体 行为。
事务终结器错误消息
通过分析语义和运行时问题来排查这些问题 错误消息。下表提供有关 Apex 调试日志中错误消息的信息。
错误信息 | 失败的上下文 | 失败原因 |
---|---|---|
不能将多个终结器附加到同一个异步Apex 工作 | 可排队的执行 | System.attachFinalizer()被多次调用 在同一个 Queueable 实例中。 |
类 {0} 必须实现终结器接口 | 可排队的执行 | 实例化的类参数 to 不实现接口。System.attachFinalizer()System.Finalizer |
System.attachFinalizer(Finalizer) 是不允许的 上下文 | 不可排队的执行 | System.attachFinalizer()在 Apex 中调用 未执行 Queueable 实例的上下文。 |
参数数量无效 | 可排队的执行 | 无效的参数数System.attachFinalizer() |
参数不能为 null | 可排队的执行 | System.attachFinalizer()以 null 调用 参数。 |
如果您有适用于 Salesforce 的 Splunk 附加组件,则可以分析错误 Splunk 日志中的消息。下表提供有关 Splunk 日志。
错误信息 | 失败原因 |
---|---|
处理可排队作业 ID 的终结器时出错: {0} | 执行终结器时出现运行时错误。此错误可以是 未处理的可捕获异常或不可捕获的异常(例如 一个 LimitException),或者不太常见的是内部系统 错误。 |
处理终结器(类名:{0})时出错 可排队作业 ID:{1}(可排队类 ID:{2}) | 执行终结器时出现运行时错误。此错误可以是 未处理的可捕获异常或不可捕获的异常(例如 一个 LimitException),或者不太常见的是内部系统 错误。 |
Apex 调度程序
要调用 Apex 类在特定时间运行,请首先实现该类的接口,然后 使用 Salesforce 用户中的“计划Apex”页面指定计划 接口或方法。SchedulableSystem.schedule
重要
Salesforce 计划在指定时间执行类。 实际执行可能会根据服务可用性延迟。
您一次只能有 100 个计划的 Apex 作业。您可以评估您当前的 通过在 Salesforce 中查看“计划作业”页面并使用 类型过滤器等于“Scheduled Apex”。还可以以编程方式查询 CronTrigger 和 CronJobDetail 对象来获取计划的 Apex 计数 工作。
如果出现以下情况,请格外小心 您计划从触发器安排课程。您必须能够 保证触发器添加的计划类不会超过限制。在 特别是,考虑 API 批量更新、导入向导、批量记录更改 用户界面,以及可以在 时间。
如果 Apex 类有一个或多个活动计划作业, 您无法通过 Salesforce 用户界面。但是,您可以启用部署以使用 使用元数据 API 激活计划作业(例如,使用 Salesforce 时) Visual Studio Code 的扩展)。请参阅中的“更改集的部署连接” Salesforce 帮助。
实现 Schedulable 接口
要安排 Apex 类定期运行,首先 编写一个实现 Salesforce 提供的接口的 Apex 类。Schedulable
调度程序以 系统 – 执行所有类,无论用户是否具有执行权限 类与否。
监视或停止计划的 Apex 作业的执行 使用 Salesforce 用户界面,从“设置”中输入“快速查找”框,然后选择“计划作业”。Scheduled Jobs该接口包含一个必须实现的方法。
Schedulableexecute
global void execute(SchedulableContext sc){}
这 实现的方法必须声明为 或 。globalpublic用 此方法实例化要调度的类。
提示
虽然这是可能的 在方法中进行额外的处理,我们建议所有处理都必须采取 放在一个单独的班级中。execute
以下示例 实现 类称为:SchedulableMergeNumbers
global class ScheduledMerge implements Schedulable {
global void execute(SchedulableContext SC) {
MergeNumbers M = new MergeNumbers();
}
}
要实现该类,请在 Developer 中执行此示例 安慰。
ScheduledMerge m = new ScheduledMerge();
String sch = '20 30 8 10 2 ?';
String jobID = System.schedule('Merge Job', sch, m);
您还可以使用 与批处理 Apex 的接口 类。下面的示例实现名为 的批处理 Apex 类的接口:SchedulableSchedulableBatchable
global class ScheduledBatchable implements Schedulable {
global void execute(SchedulableContext sc) {
Batchable b = new Batchable();
Database.executeBatch(b);
}
}
计划批处理作业的更简单方法是调用该方法,而无需实现接口。System.scheduleBatchSchedulable
在以下情况下使用 SchedulableContext 对象跟踪计划作业 这是预定的。SchedulableContext 方法返回关联的 CronTrigger 对象的 ID 将此计划作业作为字符串。您可以查询以跟踪计划作业的进度。getTriggerIDCronTrigger
自 停止执行已计划的作业时,请使用该方法和该方法返回的 ID。System.abortJobgetTriggerID
使用查询跟踪计划作业的进度
计划 Apex 作业后,您可以通过以下方式获取有关它的详细信息 在 CronTrigger 上运行 SOQL 查询。您可以检索作业的次数 run,以及计划再次运行作业的日期和时间,如下所示 例。
CronTrigger ct =
[SELECT TimesTriggered, NextFireTime
FROM CronTrigger WHERE Id = :jobID];
前面的示例假定您有一个变量,用于保存作业的 ID。该方法返回作业 ID。如果要执行此查询 在您的 Schedulable 方法中 类,可以通过调用 SchedulableContext 参数来获取当前作业的 ID 变量。假设此变量名称为 ,则 修改后的示例变为:jobIDSystem.scheduleexecutegetTriggerIdsc
CronTrigger ct =
[SELECT TimesTriggered, NextFireTime
FROM CronTrigger WHERE Id = :sc.getTriggerId()];
还可以从 CronJobDetail 获取作业的名称和作业类型 与 CronTrigger 记录关联的记录。为此,请在 Cron触发器。此示例检索具有作业名称的最新 CronTrigger 记录 并从 CronJobDetail 键入。CronJobDetail
CronTrigger job =
[SELECT Id, CronJobDetail.Id, CronJobDetail.Name, CronJobDetail.JobType
FROM CronTrigger ORDER BY CreatedDate DESC LIMIT 1];
或者,您可以直接查询 CronJobDetail 以获取作业的名称和 类型。下一个示例获取 CronTrigger 记录的作业名称和类型 在前面的示例中查询。获取对应的 CronJobDetail 记录 ID 通过表达式 CronTrigger 记录。CronJobDetail.Id
CronJobDetail ctd =
[SELECT Id, Name, JobType
FROM CronJobDetail WHERE Id = :job.CronJobDetail.Id];
获取所有 Apex 计划作业(不包括所有其他计划作业)的总数 类型,请执行以下查询。请注意,为作业类型指定了值“7”。 这与计划的 Apex 作业类型相对应。
SELECT COUNT() FROM CronTrigger WHERE CronJobDetail.JobType = '7'
测试 Apex 调度
这 以下是如何使用 Apex 调度程序进行测试的示例。
该方法启动异步 过程。测试计划的 Apex 时,必须确保计划的作业是 在对照结果进行测试之前完成。在继续测试之前,请使用测试方法和方法以确保其完成。进行的所有异步调用 在方法被收集后 系统。执行时,所有 异步进程是同步运行的。如果未在 和 方法中包含该方法,则计划作业将在测试方法结束时执行 对于使用 Salesforce API 版本 25.0 及更高版本保存的 Apex,但在早期版本中则不然。System.schedulestartTeststopTestSystem.schedulestartTeststopTestSystem.schedulestartTeststopTest此示例定义要测试的类。
global class TestScheduledApexFromTestMethod implements Schedulable {
// This test runs a scheduled job at midnight Sept. 3rd. 2042
public static String CRON_EXP = '0 0 0 3 9 ? 2042';
global void execute(SchedulableContext ctx) {
CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime
FROM CronTrigger WHERE Id = :ctx.getTriggerId()];
System.assertEquals(CRON_EXP, ct.CronExpression);
System.assertEquals(0, ct.TimesTriggered);
System.assertEquals('2042-09-03 00:00:00', String.valueOf(ct.NextFireTime));
Account a = [SELECT Id, Name FROM Account WHERE Name =
'testScheduledApexFromTestMethod'];
a.name = 'testScheduledApexFromTestMethodUpdated';
update a;
}
}
以下测试 类:
@istest
class TestClass {
static testmethod void test() {
Test.startTest();
Account a = new Account();
a.Name = 'testScheduledApexFromTestMethod';
insert a;
// Schedule the test job
String jobId = System.schedule('testBasicScheduledApex',
TestScheduledApexFromTestMethod.CRON_EXP,
new TestScheduledApexFromTestMethod());
// Get the information from the CronTrigger API object
CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered,
NextFireTime
FROM CronTrigger WHERE id = :jobId];
// Verify the expressions are the same
System.assertEquals(TestScheduledApexFromTestMethod.CRON_EXP,
ct.CronExpression);
// Verify the job has not run
System.assertEquals(0, ct.TimesTriggered);
// Verify the next time the job will run
System.assertEquals('2042-09-03 00:00:00',
String.valueOf(ct.NextFireTime));
System.assertNotEquals('testScheduledApexFromTestMethodUpdated',
[SELECT id, name FROM account WHERE id = :a.id].name);
Test.stopTest();
System.assertEquals('testScheduledApexFromTestMethodUpdated',
[SELECT Id, Name FROM Account WHERE Id = :a.Id].Name);
}
}
使用 System.schedule 方法
使用接口实现类后,使用该方法执行该类。调度程序以 系统 – 执行所有类,无论用户是否具有执行权限 类,或不类。
SchedulableSystem.schedule
注意
如果您打算安排 类。您必须能够保证触发器 添加的预定课程不会超过限制。特别要考虑 API 批量更新、导入向导、通过用户批量记录更改 接口,以及可以在 时间。
该方法采用三个参数:作业的名称、 用于表示作业计划运行的时间和日期的表达式, 和类的名称。System.schedule此表达式具有以下特征 语法:
Seconds Minutes Hours Day_of_month Month Day_of_week Optional_year
注意
Salesforce的 计划在指定时间执行类。实际执行可以是 根据服务可用性延迟。
该方法使用 用户时区作为所有计划的基础。System.schedule
以下是表达式的值:
名字 | 值 | 特殊字符 |
---|---|---|
Seconds | 0–59 | 没有 |
Minutes | 0–59 | 没有 |
Hours | 0–23 | , – * / |
Day_of_month | 1–31 | , – * ? / L W |
Month | 1-12 或以下:JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC | , – * / |
Day_of_week | 1–7 or the following:SUNMONTUEWEDTHUFRISAT | , – * ? / L # |
optional_year | null 或 1970–2099 | , – * / |
特殊字符定义如下:
特殊字符 | 描述 |
---|---|
, | 分隔值。例如,用于指定 一个多月。JAN, MAR, APR |
– | 指定范围。例如,use 指定 more than 一个月。JAN-MAR |
* | 指定所有值。例如,如果指定为 ,则作业计划为 每个月。Month* |
? | 不指定任何特定值。此选项仅可用 for 和 .它通常用于以下情况 为一个指定值,而不是为另一个指定值。Day_of_monthDay_of_week |
/ | 指定增量。斜杠前的数字指定 间隔何时开始,斜杠后的数字为 间隔量。例如,如果指定 for ,则 Apex 类每隔 每月的第五天,从 月。1/5Day_of_month |
L | 指定范围的结束(最后一个)。此选项仅 可用于 和 。当与 一起使用时,始终表示当月的最后一天,例如 1 月 31 日、2 月 29 日(闰年)等。使用时 就其本身而言,它总是意味着 或 。当与值一起使用时,它表示最后一个 当月的那一天。例如,如果指定 ,则 指定当月的最后一个星期一。不要使用范围 值替换为 结果可能出乎意料。Day_of_monthDay_of_weekDay of monthLDay_of_week7SATDay_of_week2LL |
W | 指定给定的最近的工作日(星期一至星期五) 日。此选项仅适用于 。例如,如果指定 ,而第 20 个是 星期六,上课在19日。如果指定 ,并且第一个是 星期六,课程不是在上个月上课,而是在 第三次,即下周一。Day_of_month20W1W提示用 的 和 一起指定 每月的最后一个工作日。LW |
# | 指定月份中的某一天,在 格式。 此选项仅适用于 。指定的工作日 () 之前的数字。之后的数字 指定日期 的月份。例如,指定意味着类在 每个月的第一个星期一。nthweekday#day_of_monthDay_of_week#SUN-SAT#2#1 |
表达 | 描述 |
---|---|
0 0 13 * * ? | 每天下午1点上课。 |
0 5 * * * ? | 课程每小时上课一次,整点后 5 分钟。注意Apex 不允许将作业计划多次 小时。 |
0 0 22 ? * 6L | 每个月的最后一个星期五晚上 10 点上课。 |
0 0 10 ? * MON-FRI | 上课时间为周一至周五上午 10 点。 |
0 0 20 * * ? 2010 | 在2010年,每天晚上8点上课。 |
在下面的示例中,该类实现接口。该课程计划于上午 8 点在 2月13日。ProscheduleSchedulable
Proschedule p = new Proschedule();
String sch = '0 0 8 13 2 ?';
System.schedule('One Time Pro', sch, p);
将 System.scheduleBatch 方法用于批处理作业
您可以调用该方法 将批处理作业安排为在将来的指定时间运行一次。此方法是 仅适用于批处理类,不需要接口的实现。因此,这很容易 为一次执行计划批处理作业。有关如何使用该方法的更多详细信息,请参见使用 System.scheduleBatch 方法。System.scheduleBatchSchedulableSystem.scheduleBatch
Apex Scheduler 限制
- 您一次只能有 100 个计划的 Apex 作业。您可以评估您的 通过查看 Salesforce 中的“计划作业”页面并创建 具有等于“Scheduled Apex”的类型过滤器的自定义视图。您可以 还以编程方式查询 CronTrigger 和 CronJobDetail 对象以获取 Apex 计划作业的计数。
- 每 24 小时内计划的最大 Apex 执行次数为 250,000 次或 组织中的用户许可证数乘以 200,以 大。此限制适用于整个组织,并与所有异步 Apex 共享: Batch Apex、Queueable Apex、scheduled Apex 和 future 方法。检查多少 异步 Apex 执行可用,向 REST API 资源发出请求。查看清单 REST 中的组织限制 API 开发人员指南。计入此目的的许可证类型 限制包括完整的 Salesforce 和 Salesforce Platform 用户许可证、应用程序订阅 用户许可证、仅限 Chatter 用户、身份用户和公司社区 用户。limits
Apex Scheduler 说明和最佳实践
- Salesforce 计划在指定时间执行类。实际执行 可能会根据服务可用性延迟。
- 如果您打算从触发器安排课程,请格外小心。你 必须能够保证触发器不会添加更多计划类 超过极限。具体而言,请考虑 API 批量更新、导入向导、批量记录 通过用户界面进行更改,以及可以有多个记录的所有情况 一次更新。
- 尽管可以在该方法中进行其他处理,但我们建议所有 处理必须在单独的类中进行。execute
- 计划的 Apex 不支持同步 Web 服务标注。要使 异步标注,使用 Queueable Apex,实现标记接口。如果你的 scheduled Apex 使用标记器接口执行批处理作业,支持 批处理类。请参阅使用 Batch Apex。Database.AllowsCalloutsDatabase.AllowsCallouts
- 计划在 Salesforce 服务维护停机期间运行的 Apex 作业将是 计划在服务恢复后运行,此时系统资源变为 可用。如果在发生停机时计划的 Apex 作业正在运行,则该作业是 在服务恢复后回滚并再次计划。大修后 升级时,启动计划的 Apex 作业可能会有比平时更长的延迟 因为系统使用率激增。
- 计划作业对象及其成员变量和属性将持续存在 从初始化到后续计划运行。对象状态 persists 的调用 后续作业执行。System.schedule()使用 Batch Apex,可以强制执行新的 使用新作业的序列化状态。对于 Scheduled Apex,请使用关键字,以便成员变量和 属性不会持久化。请参阅使用 transient 关键字。Database.Statefultransient
批处理Apex
开发人员现在可以使用批处理 Apex 来构建复杂的、长时间运行的流程,这些流程可以运行 在闪电平台上的数千条记录上。Batch Apex 在小型 批量记录,涵盖整个记录集,并将处理分解为 可管理的块。例如,开发人员可以构建一个运行 每晚查找超过特定日期的记录并将它们添加到 档案。或者,开发人员可以构建一个数据清理操作,该操作遍及所有 每晚的帐户和业务机会,并在必要时根据 自定义条件。
Batch Apex 公开为必须由 开发人员。可以使用 Apex 在运行时以编程方式调用批处理作业。一次只能有五个排队或活动的批处理作业。您可以评估您的 通过在 Salesforce 中查看“计划作业”页面或以编程方式使用 用于查询对象的 SOAP API。
AsyncApexJob
警告
如果您是 计划从触发器调用批处理作业。您必须能够保证 触发器添加的批处理作业不会超过限制。特别 考虑 API 批量更新、导入向导、通过用户批量记录更改 接口,以及可以在 时间。
还可以使用 Apex 调度程序以编程方式调度批处理作业,以在特定时间运行,或使用调度调度作业 Salesforce 用户界面中的 Apex 页面。有关 Schedule Apex 的更多信息 页面上,请参阅 Salesforce 联机帮助中的“安排 Apex 作业”。
批处理 Apex 接口还用于 Apex 托管共享重新计算。
有关批处理作业的详细信息,请继续使用 Batch Apex。
有关 Apex 托管共享的详细信息,请参阅了解 Apex 托管共享。
有关从批处理 Apex 触发平台事件的详细信息,请参阅从批处理 Apex 触发平台事件
- 使用 Batch Apex
- 从 Batch Apex 触发平台事件 Batch Apex
类可以在遇到错误或异常时触发平台事件。侦听事件的客户端可以获取可操作的信息,例如事件失败的频率以及失败时哪些记录在范围内。对于 Salesforce Platform 内部错误和其他无法捕获的 Apex 异常(如 LimitExceptions),也会触发事件,这些异常是由达到调控器限制引起的。
使用 Batch Apex
要使用批处理 Apex,请编写一个实现 Salesforce 提供的接口的 Apex 类,然后调用 以编程方式类。Database.Batchable
要监控或停止批处理 Apex 作业的执行,请从“设置”中输入“快速查找”框,然后选择“Apex 作业”。Apex Jobs
实现 Database.Batchable 接口
该接口包含 必须实现的三种方法。
Database.Batchable
- start方法:
public (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {}
自 收集要传递给接口方法的记录或对象,在 批处理 Apex 作业。此方法返回一个对象或 iterable,包含传递给 工作。executestartDatabase.QueryLocator使用简单查询 () 生成范围时 批处理作业中的对象,请使用该对象。如果使用对象,则调控器 SOQL 查询检索的记录总数限制为 绕过。例如,Account 对象的批处理 Apex 作业可以 全部返回 A 组织中的帐户记录(最多 5000 万条记录)。另一个例子是 对 Contact 对象的共享重新计算,该对象为所有客户记录返回 在组织中。SELECTDatabase.QueryLocatorQueryLocatorQueryLocatorQueryLocator使用 iterable 为 批处理作业。您还可以使用迭代对象来创建自己的自定义 循环访问列表的过程。重要如果您使用 可迭代,记录总数的调控器限制 由 SOQL 查询检索到的仍会强制执行。欲了解更多信息 将可迭代对象用于批处理作业,请参阅 Batch Apex 最佳实践 - execute方法:
public void execute(Database.BatchableContext BC, list<P>){}
自 对每个数据块进行所需的处理,请使用该方法。此方法是 为传递给它的每批记录调用。execute此方法 采取以下措施:- 对对象的引用。Database.BatchableContext
- sObject 的列表,例如 或参数化的列表 类型。如果您使用的是 ,请使用返回的列表。List<sObject>Database.QueryLocator
- finish方法:
public void finish(Database.BatchableContext BC){}
自 发送确认电子邮件或执行后处理操作,请使用该方法。此方法 在处理完所有批处理后调用。finish
批处理 Apex 作业的每次执行都被视为离散事务。例如 包含 1,000 条记录且在没有可选参数的情况下执行的批处理 Apex 作业被视为 200 条记录的 5 个事务 每。每个事务都会重置 Apex 调控器限制。如果第一个 事务成功,但第二个事务失败,第一个事务中的数据库更新 事务不会回滚。scopeDatabase.executeBatch
用 Database.BatchableContext
都 接口中的方法需要对对象的引用。使用此对象来跟踪 批处理作业的进度。Database.BatchableDatabase.BatchableContext以下是具有该对象的实例方法:
Database.BatchableContext
名字 | 参数 | 返回 | 描述 |
---|---|---|---|
getJobID | 编号 | 以字符串形式返回与此批处理作业关联的 AsyncApexJob 对象的 ID。使用这个 方法跟踪批处理作业中记录的进度。你 还可以将此 ID 与 System.abortJob 方法一起使用。 |
以下示例使用 查询与批处理关联的 工作。Database.BatchableContextAsyncApexJob
public void finish(Database.BatchableContext BC){
// Get the ID of the AsyncApexJob representing this batch job
// from Database.BatchableContext.
// Query the AsyncApexJob object to retrieve the current job's information.
AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,
TotalJobItems, CreatedBy.Email
FROM AsyncApexJob WHERE Id =
:BC.getJobId()];
// Send an email to the Apex job's submitter notifying of job completion.
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Sharing Recalculation ' + a.Status);
mail.setPlainTextBody
('The batch Apex job processed ' + a.TotalJobItems +
' batches with '+ a.NumberOfErrors + ' failures.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
使用 Database.QueryLocator 定义 范围
该方法可以返回一个对象,该对象包含 要在批处理作业或可迭代对象中使用的记录。startDatabase.QueryLocator以下示例使用 :
Database.QueryLocator
public class SearchAndReplace implements Database.Batchable<sObject>{
public final String Query;
public final String Entity;
public final String Field;
public final String Value;
public SearchAndReplace(String q, String e, String f, String v){
Query=q; Entity=e; Field=f;Value=v;
}
public Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
public void execute(Database.BatchableContext BC, List<sObject> scope){
for(sobject s : scope){
s.put(Field,Value);
}
update scope;
}
public void finish(Database.BatchableContext BC){
}
}
使用 Batch Apex 中的可迭代对象来定义 范围
该方法可以返回一个对象,该对象包含 要在批处理作业或可迭代对象中使用的记录。使用 iterable 单步执行 更容易退回物品。startDatabase.QueryLocator
public class batchClass implements Database.batchable{
public Iterable start(Database.BatchableContext info){
return new CustomAccountIterable();
}
public void execute(Database.BatchableContext info, List<Account> scope){
List<Account> accsToUpdate = new List<Account>();
for(Account a : scope){
a.Name = 'true';
a.NumberOfEmployees = 70;
accsToUpdate.add(a);
}
update accsToUpdate;
}
public void finish(Database.BatchableContext info){
}
}
使用 Database.executeBatch 方法提交批处理作业
您可以使用该方法 以编程方式开始批处理作业。
Database.executeBatch
重要
当您调用 时,Salesforce 会将流程添加到 队列。实际执行可能会根据服务延迟 可用性。Database.executeBatch该方法采用两个参数:
Database.executeBatch
- 实现接口的类的实例。Database.Batchable
- 可选参数 。此参数指定 要传递到方法中的记录。scopeexecute使用这个 参数,当您对要传递的每条记录执行许多操作时 并且正在遇到调控器限制。通过限制数量 记录,则限制了每笔交易的操作。此值 必须大于零。如果批处理类的方法返回 QueryLocator, 可选范围参数的最大值为startDatabase.executeBatch2,000.如果设置为更高的值,则 Salesforce 将 QueryLocator 返回的记录分块为更小的批次 最多 2,000 条记录。如果批处理的方法 类返回一个可迭代对象,scope 参数值没有上限。 但是,如果使用较高的数字,则可能会遇到其他限制。这 最佳示波器大小是 2000 的系数,例如 100、200、400 和 等等。start
该方法返回 AsyncApexJob 对象的 ID,可用于跟踪作业的进度。 例如:
Database.executeBatch
ID batchprocessid = Database.executeBatch(reassign);
AsyncApexJob aaj = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors
FROM AsyncApexJob WHERE ID =: batchprocessid ];
还可以将此 ID 与 System.abortJob 方法一起使用。
有关详细信息,请参阅对象中的 AsyncApexJob Salesforce 参考。
在 Apex Flex 中保存批处理作业 队列
使用 Apex flex 队列,您最多可以提交 100 个批次 工作。结果如下。
Database.executeBatch
- 批处理作业将放置在 Apex flex 队列中,其状态设置为 。Holding
- 如果 Apex flex 队列的最大作业数为 100 个,则抛出一个并且不添加该作业 到队列。Database.executeBatchLimitException
注意
如果您的组织未启用 Apex flex 队列,请将批处理作业添加到 具有状态的批处理作业队列。如果 已达到排队或活动批处理作业的并发限制,引发了 A,但作业未 排队。Database.executeBatchQueuedLimitException
对 Apex Flex 队列中的作业重新排序
提交时 作业的状态为 ,可以 在 Salesforce 用户界面中对它们重新排序,以控制哪些批处理作业是 首先处理。为此,请从“设置”中输入“快速查找”框,然后选择“Apex Flex” 队列。HoldingApex Flex Queue
或者,您可以使用 Apex 方法重新排序 Flex 队列中的批处理作业。若要将作业移动到新位置,请调用 System.FlexQueue 方法之一。将作业 ID 传递给该方法, 如果适用,则为移动作业的新职位旁边的作业 ID。为 例:
Boolean isSuccess = System.FlexQueue.moveBeforeJob(jobToMoveId, jobInQueueId);
您可以对 Apex flex 队列中的作业重新排序,以确定作业的优先级。为 例如,您可以将批处理作业移动到保留队列中的第一个位置 当资源可用时,首先处理。否则,将处理作业 “先进先出”——按提交顺序排列。
当系统 资源变为可用,系统将从 Apex flex 队列,并将其移动到批处理作业队列。该系统可以处理多达 每个组织同时有 5 个排队或活动的作业。这些状态 将作业更改从 移动到 。排队的作业在系统执行时执行 已准备好处理新作业。您可以在 Apex 作业上监控排队的作业 页。HoldingQueued
批处理作业状态
下表列出了批处理作业的所有可能状态以及 每个的描述。
地位 | 描述 |
---|---|
占有 | 作业已提交并保留在 Apex flex 队列中,直到 系统资源可用于对作业进行排队 加工。 |
排队 | 作业正在等待执行。 |
准备 | 方法 job 已被调用。此状态可能会持续几分钟,具体取决于 关于批量记录的大小。start |
加工 | 正在处理作业。 |
中止 | 用户已中止作业。 |
完成 | 作业已完成,但有或没有失败。 |
失败 | 作业遇到系统故障。 |
使用 System.scheduleBatch 方法
您可以使用该方法 将批处理作业安排为在将来运行一次。System.scheduleBatch该方法采用以下参数。
System.scheduleBatch
- 实现接口的类的实例。Database.Batchable
- 作业名称。
- 作业开始执行的时间间隔(以分钟为单位)。
- 可选范围值。此参数指定要 传入方法。使用这个 参数,当您对要传递的每条记录执行许多操作时 并且正在遇到调控器限制。通过限制数量 记录,则限制了每笔交易的操作。此值 必须大于零。如果批处理类的方法返回 QueryLocator, 可选范围参数的最大值为executestartDatabase.executeBatch2,000.如果设置为更高的值,则 Salesforce 将 QueryLocator 返回的记录分块为更小的批次 最多 2,000 条记录。如果批处理的方法 类返回一个可迭代对象,scope 参数值没有上限。 但是,如果使用较高的数字,则可能会遇到其他限制。这 最佳示波器大小是 2000 的系数,例如 100、200、400 和 等等。start
该方法返回计划的作业 ID (CronTrigger ID)。System.scheduleBatch此示例将批处理作业安排在 60 分钟内运行 现在通过调用 .这 示例向此方法传递批处理类(变量)、作业名称和时间的实例 间隔 60 分钟。可选参数已 省略。该方法返回计划作业 ID,用于查询 CronTrigger 获取相应计划的状态 工作。
System.scheduleBatchreassignscope
String cronID = System.scheduleBatch(reassign, 'job example', 60);
CronTrigger ct = [SELECT Id, TimesTriggered, NextFireTime
FROM CronTrigger WHERE Id = :cronID];
// TimesTriggered should be 0 because the job hasn't started yet.
System.assertEquals(0, ct.TimesTriggered);
System.debug('Next fire time: ' + ct.NextFireTime);
// For example:
// Next fire time: 2013-06-03 13:31:23
有关详细信息,请参阅对象中的 CronTrigger Salesforce 参考。
注意
需要注意的一些事项:System.scheduleBatch
- 当您调用 , Salesforce 计划在指定时间执行作业。实际 在该时间或之后执行,具体取决于服务 可用性。System.scheduleBatch
- 调度程序作为系统运行——所有类都已执行,无论 用户是否有权执行该类。
- 触发作业的计划时,系统会将批处理作业排入队列 加工。如果在您的组织中启用了 Apex flex 队列,则批处理作业为 添加到 Flex 队列的末尾。有关更多信息,请参阅将批处理作业保存在 Apex Flex 队列。
- 所有计划的 Apex 限制都适用于批处理作业 计划使用 。批处理作业排队后(使用 状态为 或 ),所有批处理作业限制都适用 并且作业不再计入计划的 Apex 限制。System.scheduleBatchHoldingQueued
- 调用此方法后,在批处理作业开始之前,可以使用 返回计划的作业 ID 以使用 System.abortJob 方法中止计划作业。
Batch Apex 示例
以下示例使用 :
Database.QueryLocator
public class UpdateAccountFields implements Database.Batchable<sObject>{
public final String Query;
public final String Entity;
public final String Field;
public final String Value;
public UpdateAccountFields(String q, String e, String f, String v){
Query=q; Entity=e; Field=f;Value=v;
}
public Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
public void execute(Database.BatchableContext BC,
List<sObject> scope){
for(Sobject s : scope){s.put(Field,Value);
} update scope;
}
public void finish(Database.BatchableContext BC){
}
}
您可以使用以下代码调用上一个 类。
// Query for 10 accounts
String q = 'SELECT Industry FROM Account LIMIT 10';
String e = 'Account';
String f = 'Industry';
String v = 'Consulting';
Id batchInstanceId = Database.executeBatch(new UpdateAccountFields(q,e,f,v), 5);
若要排除已删除但仍在回收站中的帐户或发票, 包含在 SOQL 查询中 WHERE 子句,如这些修改后的 样品。
isDeleted=false
// Query for accounts that aren't in the Recycle Bin
String q = 'SELECT Industry FROM Account WHERE isDeleted=false LIMIT 10';
String e = 'Account';
String f = 'Industry';
String v = 'Consulting';
Id batchInstanceId = Database.executeBatch(new UpdateAccountFields(q,e,f,v), 5);
// Query for invoices that aren't in the Recycle Bin
String q =
'SELECT Description__c FROM Invoice_Statement__c WHERE isDeleted=false LIMIT 10';
String e = 'Invoice_Statement__c';
String f = 'Description__c';
String v = 'Updated description';
Id batchInstanceId = Database.executeBatch(new UpdateInvoiceFields(q,e,f,v), 5);
以下类使用批处理 Apex 重新分配特定用户拥有的所有帐户 给其他用户。
public class OwnerReassignment implements Database.Batchable<sObject>{
String query;
String email;
Id toUserId;
Id fromUserId;
public Database.querylocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);}
public void execute(Database.BatchableContext BC, List<sObject> scope){
List<Account> accns = new List<Account>();
for(sObject s : scope){Account a = (Account)s;
if(a.OwnerId==fromUserId){
a.OwnerId=toUserId;
accns.add(a);
}
}
update accns;
}
public void finish(Database.BatchableContext BC){
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] {email});
mail.setReplyTo('batch@acme.com');
mail.setSenderDisplayName('Batch Processing');
mail.setSubject('Batch Process Completed');
mail.setPlainTextBody('Batch Process has completed');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
使用以下命令执行上一个中的类 例。
OwnerReassignment
OwnerReassignment reassign = new OwnerReassignment();
reassign.query = 'SELECT Id, Name, Ownerid FROM Account ' +
'WHERE ownerid=\'' + u.id + '\'';
reassign.email='admin@acme.com';
reassign.fromUserId = u;
reassign.toUserId = u2;
ID batchprocessid = Database.executeBatch(reassign);
以下是用于删除的批处理 Apex 类的示例 记录。
public class BatchDelete implements Database.Batchable<sObject> {
public String query;
public Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
public void execute(Database.BatchableContext BC, List<sObject> scope){
delete scope;
DataBase.emptyRecycleBin(scope);
}
public void finish(Database.BatchableContext BC){
}
}
此代码调用批处理 Apex 类删除旧文档。指定的查询选择要删除的文档 指定文件夹中且早于指定文件夹的所有文档 日期。接下来,示例调用批处理 工作。
BatchDelete
BatchDelete BDel = new BatchDelete();
Datetime d = Datetime.now();
d = d.addDays(-1);
// Replace this value with the folder ID that contains
// the documents to delete.
String folderId = '00lD000000116lD';
// Query for selecting the documents to delete
BDel.query = 'SELECT Id FROM Document WHERE FolderId=\'' + folderId +
'\' AND CreatedDate < '+d.format('yyyy-MM-dd')+'T'+
d.format('HH:mm')+':00.000Z';
// Invoke the batch job.
ID batchprocessid = Database.executeBatch(BDel);
System.debug('Returned batch process ID: ' + batchProcessId);
在 Batch Apex 中使用标注
要在批处理 Apex 中使用标注,请在类中指定 定义。为 例:
Database.AllowsCallouts
public class SearchAndReplace implements Database.Batchable<sObject>,
Database.AllowsCallouts{
}
标注包括使用 KEYWORD 定义的 HTTP 请求和方法。webservice
在 Batch Apex 中使用状态
批处理 Apex 作业的每次执行都被视为离散事务。例如 包含 1,000 条记录且在没有可选参数的情况下执行的批处理 Apex 作业被视为 200 个事务的 5 个事务 记录每个。scope
如果在 类定义,您可以在这些事务中维护状态。使用 时,仅限实例成员 变量在事务之间保留其值。静态成员变量不会 保留其值并在交易之间重置。维护状态很有用 用于在处理记录时对记录进行计数或汇总。例如,假设你的 作业流程商机记录。您可以定义一种方法来聚合商机的总数 处理时的金额。Database.StatefulDatabase.Statefulexecute
如果未指定 ,则所有 静态和实例成员变量将设置回其原始值。Database.Stateful以下示例将自定义字段total__c汇总为 记录是 处理。
public class SummarizeAccountTotal implements
Database.Batchable<sObject>, Database.Stateful{
public final String Query;
public integer Summary;
public SummarizeAccountTotal(String q){Query=q;
Summary = 0;
}
public Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator(query);
}
public void execute(
Database.BatchableContext BC,
List<sObject> scope){
for(sObject s : scope){
Summary = Integer.valueOf(s.get('total__c'))+Summary;
}
}
public void finish(Database.BatchableContext BC){
}
}
此外,还可以指定一个变量来访问类的初始状态。你 可以使用此变量与方法的所有实例共享初始状态。为 例:
Database.Batchable
// Implement the interface using a list of Account sObjects
// Note that the initialState variable is declared as final
public class MyBatchable implements Database.Batchable<sObject> {
private final String initialState;
String query;
public MyBatchable(String intialState) {
this.initialState = initialState;
}
public Database.QueryLocator start(Database.BatchableContext BC) {
// Access initialState here
return Database.getQueryLocator(query);
}
public void execute(Database.BatchableContext BC,
List<sObject> batch) {
// Access initialState here
}
public void finish(Database.BatchableContext BC) {
// Access initialState here
}
}
仅存储类的初始状态。您不能使用它在 在批处理作业执行期间类的实例。例如,如果将 in 的值,即已处理记录的第二个块 无法访问新值。只有初始值是可访问的。initialStateinitialStateexecute
测试 Batch Apex
在测试批处理 Apex 时,您可以测试 该方法仅执行一次。使用该方法的参数来限制 传递到方法中的记录 以确保您不会遇到调控器限制。executescopeexecuteBatchexecute
该方法启动一个异步进程。测试时 批处理 Apex,确保异步处理的批处理作业已完成 在对照结果进行测试之前。使用测试方法和方法,确保在继续操作之前完成 你的测试。executeBatchstartTeststopTestexecuteBatch系统将收集在该方法之后进行的所有异步调用。 执行时,全部异步 进程是同步运行的。如果未在 和 方法中包含该方法,则批处理作业将在测试结束时执行 方法。此执行顺序适用于使用 API 版本 25.0 保存的 Apex,并且 更高版本,但不适用于早期版本。startTeststopTestexecuteBatchstartTeststopTest
对于使用 API 版本 22.0 及更高版本保存的 Apex, 在执行测试调用的批处理 Apex 作业期间发生的异常 方法被传递给调用测试方法。因此,这些异常 导致测试方法失败。如果要在测试中处理异常 方法,将代码括在 AND 语句中。将块放在方法之后。但是,保存了 Apex 使用 Apex 版本 21.0 及更早版本,此类异常不会传递给 测试方法,并且不会导致测试方法失败。trycatchcatchstopTest
注意
异步调用, 例如 或 ,在 、 块中调用,则不计入 排队的作业。@futureexecuteBatchstartTeststopTest下面的示例测试 OwnerReassignment 类。
public static testMethod void testBatch() {
user u = [SELECT ID, UserName FROM User
WHERE username='testuser1@acme.com'];
user u2 = [SELECT ID, UserName FROM User
WHERE username='testuser2@acme.com'];
String u2id = u2.id;
// Create 200 test accounts - this simulates one execute.
// Important - the Salesforce test framework only allows you to
// test one execute.
List <Account> accns = new List<Account>();
for(integer i = 0; i<200; i++){
Account a = new Account(Name='testAccount'+ i,
Ownerid = u.ID);
accns.add(a);
}
insert accns;
Test.StartTest();
OwnerReassignment reassign = new OwnerReassignment();
reassign.query='SELECT ID, Name, Ownerid ' +
'FROM Account ' +
'WHERE OwnerId=\'' + u.Id + '\'' +
' LIMIT 200';
reassign.email='admin@acme.com';
reassign.fromUserId = u.Id;
reassign.toUserId = u2.Id;
ID batchprocessid = Database.executeBatch(reassign);
Test.StopTest();
System.AssertEquals(
database.countquery('SELECT COUNT()'
+' FROM Account WHERE OwnerId=\'' + u2.Id + '\''),
200);
}
}
使用 和 方法执行以下操作 在测试上下文中对无操作作业进行排队和重新排序。System.Test.enqueueBatchJobsSystem.Test.getFlexQueueOrder
Batch Apex 限制
请记住以下调速器限制和其他限制 用于批处理 Apex。
- 最多可以同时排队或活动 5 个批处理作业。
- 最多可以有 100 个批处理作业 保存在 Apex flex 队列中。Holding
- 在运行测试中,您最多可以提交 5 个批处理作业。
- 每 24 小时期间批量执行 Apex 方法的最大次数为 250,000,或组织中的用户许可证数量乘以 200 – 以较大者为准。方法执行包括 、 和 方法的执行。此限制适用于您的 整个组织,并与所有异步 Apex 共享:Batch Apex、Queueable Apex、计划的 Apex 和未来的方法。检查有多少个异步 Apex 执行可用,向 REST API 资源发出请求。请参阅 REST API 开发人员指南中的列出组织限制。许可证 计入此限制的类型包括完整的 Salesforce 和 Salesforce 平台用户许可证、应用程序订阅用户许可证、仅限 Chatter 用户、 标识用户和公司社区用户。startexecutefinishlimits
- 最多5000万记录可以是 在对象中返回。如果超过 5000 万条记录 返回,批处理作业将立即终止并标记为 失败。Database.QueryLocator
- 如果批处理类的方法返回 QueryLocator, 可选范围参数的最大值为startDatabase.executeBatch2,000.如果设置为更高的值,则 Salesforce 将 QueryLocator 返回的记录分块为更小的批次 最多 2,000 条记录。如果批处理的方法 类返回一个可迭代对象,scope 参数值没有上限。 但是,如果使用较高的数字,则可能会遇到其他限制。这 最佳示波器大小是 2000 的系数,例如 100、200、400 和 等等。start
- 如果未使用可选参数指定大小 之 Salesforce 将该方法返回的记录分块为scopeDatabase.executeBatchstart200记录。然后,系统将每个批次传递给该方法。Apex 调速器限制 在每次执行 时重置。executeexecute
- 、 和 方法最多可以实现 100 个 每个标注。startexecutefinish
- 一个组织中一次只能运行一个批处理 Apex 作业的方法。尚未启动的批处理作业 但仍保留在队列中,直到它们开始。此限制不 导致任何批处理作业失败,并且批处理 Apex 作业的方法仍并行运行,如果 多个作业正在运行。startexecute
- 在 SOQL 查询中使用 更新期间的锁定记录不适用于 Batch Apex。FOR UPDATE
- 游标和相关查询结果的有效期为 2 天,包括 导致嵌套查询。有关更多信息,请参阅 API 查询游标限制。
Batch Apex 最佳实践
- 如果计划从触发器调用批处理作业,请格外小心。你 必须能够保证触发器添加的批处理作业不会超过 限制。具体而言,请考虑 API 批量更新、导入向导、批量记录 通过用户界面进行更改,以及多个记录可以更改的所有情况 一次更新。
- 当您调用 , Salesforce 仅将作业放入队列中。实际执行可能会延迟 基于服务可用性。Database.executeBatch
- 测试批处理 Apex 时,只能测试该方法的一次执行。使用方法的参数来限制传入该方法的记录数,以确保 没有遇到调速器限制。executescopeexecuteBatchexecute
- 该方法启动一个 异步进程。测试批处理 Apex 时,请确保 异步处理的批处理作业在针对 结果。使用 Test 方法和围绕该方法确保它 在继续测试之前完成。executeBatchstartTeststopTestexecuteBatch
- 与类一起使用 如果要在作业之间共享实例成员变量或数据,请进行定义 交易。否则,所有成员变量都将重置为其初始状态 在每笔交易开始时。Database.Stateful
- 声明为 not 的方法 在实现接口的类中允许。futureDatabase.Batchable
- 声明为 can’t be 的方法 从批处理 Apex 类调用。future
- 运行批处理 Apex 作业时,将向以下用户发送电子邮件通知: 提交了批处理作业。如果代码包含在托管包中,并且 订阅组织正在运行批处理作业,通知将发送到 Apex 异常通知中列出的收件人 收件人字段。
- 每个方法执行都使用标准的 governor limits 匿名块, Visualforce 控制器或 WSDL 方法。
- 每个批处理 Apex 调用都会创建一条记录。构造 SOQL 查询以检索 作业的状态、错误数、进度和提交者,请使用记录的 ID。AsyncApexJobAsyncApexJob有关该对象的详细信息,请参阅 Salesforce 对象参考中的 AsyncApexJob。AsyncApexJob
- 对于每 10,000 条记录, Apex 创建记录 供内部使用的类型。 查询所有记录时,建议使用该字段筛选出类型的记录。否则,查询将返回 每 10,000 条记录多一条记录。有关该对象的详细信息,请参阅 Salesforce 对象参考中的 AsyncApexJob。AsyncApexJobAsyncApexJobBatchApexWorkerAsyncApexJobBatchApexWorkerJobTypeAsyncApexJobAsyncApexJob
- 所有实现的接口方法都必须定义为 或 。Database.Batchablepublicglobal
- 对于共享重新计算,建议方法删除,然后重新创建 批处理中记录的所有 Apex 托管共享。此过程可确保 共享准确完整。execute
- 在 Salesforce 服务维护停机之前排队的批处理作业仍保留在 队列。服务停机结束后,当系统资源可用时, 将执行排队的批处理作业。如果批处理作业在停机时正在运行 发生时,批处理执行将在服务后回滚并重新启动 回来了。
- 如果可能,请尽量减少批次数。Salesforce 使用基于队列的 用于处理来自未来方法等来源的异步过程的框架 和批处理 Apex。此队列用于平衡跨 组织。如果超过 2,000 个未处理的请求来自单个 组织在队列中,来自同一的任何其他请求 当队列处理来自其他组织的请求时,组织将被延迟 组织。
- 确保批处理作业尽可能快地执行。为确保快速执行 批处理作业,最大限度地减少 Web 服务标注时间,并优化 批处理 Apex 代码。批处理作业执行的时间越长,其他排队的可能性就越大 当队列中有许多作业时,作业会延迟。
- 如果将批处理 Apex 与通过 OData 访问外部对象 Salesforce Connect 适配器:Database.QueryLocator
- 在外部数据源上启用请求行计数,并且每个 来自外部系统的响应必须包括总行数 的结果集。
- 建议在外部数据上启用服务器驱动的分页 源并让外部系统确定页面大小和批次 大型结果集的边界。通常,服务器驱动的分页 可以调整批次边界以适应不断变化的数据集 比客户端驱动的寻呼更有效。当服务器驱动时 在外部数据源 OData 上禁用分页 适配器控制分页行为(客户端驱动)。如果 外部对象记录被添加到外部系统,而 作业运行,其他记录可以处理两次。如果外部 在执行作业时,将从外部系统中删除对象记录 运行,可以跳过其他记录。
- 在外部数据上启用服务器驱动分页时 source,运行时的批处理大小为以下较小值:
- 参数中指定的批大小。 默认值为 200 条记录。scopeDatabase.executeBatch
- 外部系统返回的页面大小。我们建议 将外部系统设置为返回 200 的页面大小 或更少的记录。
- 当方法通过 子查询。避免在 a 中出现关系子查询允许批处理作业使用更快的分块 实现。如果方法 返回一个可迭代对象或一个具有关系子查询的对象,批处理作业使用较慢的非分块, 实现。例如,如果在 中使用以下查询,则批处理作业使用较慢的 由于关系子查询而实现:startQueryLocatorQueryLocatorstartQueryLocatorQueryLocator
SELECT Id, (SELECT id FROM Contacts) FROM Account
更好的策略是从执行中单独执行子查询 方法,允许批处理作业使用更快的分块运行 实现。 - 若要在批处理作业中实现记录锁定,可以重新查询记录 在 execute() 方法中,如有必要,请使用 FOR UPDATE。这确保了 批处理作业中的 DML 不会覆盖任何冲突的更新。重新查询 记录,只需在批处理作业的主查询定位符中选择 Id 字段即可。
链接批处理作业
从 API 版本 26.0 开始,您可以从现有的 批处理作业,将作业链接在一起。链接批处理作业以在另一个作业之后启动作业 完成以及作业需要批处理时(例如处理大型时) 数据量。否则,如果不需要批处理,请考虑使用 Queueable Apex。
可以通过调用 或 从当前批处理类的方法链接批处理作业。新的批处理作业将开始 当前批处理作业完成后。Database.executeBatchSystem.scheduleBatchfinish
对于以前的 API 版本,您无法从任何批处理 Apex 方法调用或调用。的版本 used 是正在运行的批处理类的版本,该批处理类启动或计划另一个 批处理作业。如果 运行 Batch 类会调用帮助程序类中的方法以启动 Batch 作业,即 API 帮助程序类的版本无关紧要。Database.executeBatchSystem.scheduleBatchfinish
从 Batch Apex 触发平台事件
Batch Apex 类可以在以下情况下触发平台事件 遇到错误或异常。侦听事件的客户端可以获取可操作的 信息,例如事件失败的频率以及哪些记录在 故障时间。对于Salesforce Platform内部错误和其他 无法捕获的 Apex 异常,例如 LimitExceptions,这些异常是由 调速器限制。
事件消息提供比 Apex Jobs UI 更精细的错误跟踪。它包括 正在处理的记录 ID、异常类型、异常消息和堆栈跟踪。你 还可以合并自定义处理和失败重试逻辑。您可以调用自定义 来自此类事件的任何触发器的 Apex 逻辑,因此 Apex 开发人员可以构建 自定义日志记录或自动重试处理等功能。
有关订阅平台事件的信息,请参阅订阅平台事件。
BatchApexErrorEvent 对象表示与批处理 Apex 关联的平台事件 类。此对象在 API 版本 44.0 及更高版本中可用。如果 、 或 方法 批处理 Apex 作业遇到未处理的异常,将触发平台事件。有关详细信息,请参阅《平台事件开发人员指南》中的 BatchApexErrorEvent。startexecutefinishBatchApexErrorEvent要触发平台事件,批处理 Apex 类声明必须实现 Database.RaisesPlatformEvents 接口。
public with sharing class YourSampleBatchJob implements Database.Batchable<SObject>,
Database.RaisesPlatformEvents{
// class implementation
}
例
此示例创建一个触发器,以确定哪些帐户在批处理中失败 交易。自定义字段 Dirty__c 指示该帐户是失败批次之一 ExceptionType__c表示遇到的异常。JobScope 和 ExceptionType 是 BatchApexErrorEvent 中的字段 对象。
trigger MarkDirtyIfFail on BatchApexErrorEvent (after insert) {
Set<Id> asyncApexJobIds = new Set<Id>();
for(BatchApexErrorEvent evt:Trigger.new){
asyncApexJobIds.add(evt.AsyncApexJobId);
}
Map<Id,AsyncApexJob> jobs = new Map<Id,AsyncApexJob>(
[SELECT id, ApexClass.Name FROM AsyncApexJob WHERE Id IN :asyncApexJobIds]
);
List<Account> records = new List<Account>();
for(BatchApexErrorEvent evt:Trigger.new){
//only handle events for the job(s) we care about
if(jobs.get(evt.AsyncApexJobId).ApexClass.Name == 'AccountUpdaterJob'){
for (String item : evt.JobScope.split(',')) {
Account a = new Account(
Id = (Id)item,
ExceptionType__c = evt.ExceptionType,
Dirty__c = true
);
records.add(a);
}
}
}
update records;
}
测试从 Batch Apex 作业发布的 BatchApexErrorEvent 消息
使用方法 以传送由失败的批处理 Apex 作业发布的事件消息。使用 and 语句块执行 批处理作业。Test.getEventBus().deliver()Test.startTest()Test.stopTest()
此代码片段演示如何执行批处理 Apex 作业并传递事件消息。它 在 之后执行批处理作业。此批处理作业发布 BatchApexErrorEvent 消息 当通过实现 发生故障时。运行后,将添加一个单独的语句,以便它可以传递 BatchApexErrorEvent。Test.stopTest()Database.RaisesPlatformEventsTest.stopTest()Test.getEventBus().deliver()
try {
Test.startTest();
Database.executeBatch(new SampleBatchApex());
Test.stopTest();
// Batch Apex job executes here
} catch(Exception e) {
// Catch any exceptions thrown in the batch job
}
// The batch job fires BatchApexErrorEvent if it fails, so deliver the event.
Test.getEventBus().deliver();
注意
如果下游进程发布了进一步的平台事件,请添加以交付 每个进程的事件消息。例如,如果一个平台事件触发,则 处理来自 Apex 作业的事件,发布另一个平台事件,添加语句 传递事件消息。Test.getEventBus().deliver();Test.getEventBus().deliver();
未来方法
将来的方法在后台异步运行。您可以调用 future 方法 执行长时间运行的操作,例如对外部 Web 服务或任何 您希望在自己的线程中,在自己的时间运行的操作。您还可以使用 future 对不同 sObject 类型进行 DML 操作隔离的方法,以防止混合 DML 错误。每个将来的方法都排队,并在系统资源可用时执行。 这样,代码的执行就不必等待 长时间运行的操作。使用未来方法的一个好处是,某些调速器限制 更高,例如 SOQL 查询限制和堆大小限制。
要定义将来的方法,只需使用注释对其进行注释,如下所示。future
global class FutureClass
{
@future
public static void myFutureMethod()
{
// Perform some operations
}
}
带有注解的方法必须是 static 方法,并且只能返回 void 类型。指定的参数必须是 基元数据类型、基元数据类型的数组或基元数据的集合 类型。带有注解的方法不能 将 sObjects 或 objects 作为参数。futurefuture
sObjects 不能作为参数传递给未来方法的原因是 sObject 可以在调用方法的时间和执行方法的时间之间更改。在 在这种情况下,future 方法会获取旧的 sObject 值并可以覆盖它们。工作 替换为数据库中已存在的 sObject,而是传递 sObject ID(或 集合),并使用该 ID 对最新记录执行查询。这 以下示例演示如何使用 ID 列表执行此操作。
global class FutureMethodRecordProcessing
{
@future
public static void processRecords(List<ID> recordIds)
{
// Get those records based on the IDs
List<Account> accts = [SELECT Name FROM Account WHERE Id IN :recordIds];
// Process records
}
}
下面是一个 future 方法的骨架示例,该方法对 外部服务。请注意,注释需要一个额外的参数 () 来指示标注是 允许。要了解有关标注的更多信息,请参阅使用 Apex 调用标注。callout=true
global class FutureMethodExample
{
@future(callout=true)
public static void getStockQuotes(String acctName)
{
// Perform a callout to an external service
}
}
插入具有非 null 角色的用户必须在与 DML 不同的线程中完成 对其他 sObject 的操作。在此示例中,类中定义的 future 方法执行 首席运营官角色。这种未来的方法需要在 组织。中的方法插入一个帐户并调用 未来的方法,.insertUserWithRoleUtiluseFutureMethodMixedDMLFutureinsertUserWithRole
此类包含 插入具有非 null 角色的用户。Util
public class Util {
@future
public static void insertUserWithRole(
String uname, String al, String em, String lname) {
Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
UserRole r = [SELECT Id FROM UserRole WHERE Name='COO'];
// Create new user with a non-null user role ID
User u = new User(alias = al, email=em,
emailencodingkey='UTF-8', lastname=lname,
languagelocalekey='en_US',
localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
timezonesidkey='America/Los_Angeles',
username=uname);
insert u;
}
}
此类包含调用已定义的未来方法的 main 方法 以前。
public class MixedDMLFuture {
public static void useFutureMethod() {
// First DML operation
Account a = new Account(Name='Acme');
insert a;
// This next operation (insert a user with a role)
// can't be mixed with the previous insert unless
// it is within a future method.
// Call future method to insert a user with a role.
Util.insertUserWithRole(
'mruiz@awcomputing.com', 'mruiz',
'mruiz@awcomputing.com', 'Ruiz');
}
}
可以像调用任何其他方法一样调用将来的方法。然而,未来 方法不能调用另一个 future 方法。
带有注解的方法具有 以下限制:future
- 在批处理和未来上下文中不超过 0;50 个可排队的上下文方法调用 每个 Apex 调用。异步调用,例如 或 , 在 、 块中调用,不计入您的限制 表示排队作业的数量。@futureexecuteBatchstartTeststopTest注意将多个未来方法扇出 不建议将可排队作业作为做法,因为它可以快速添加大量 异步队列的 future 方法。请求处理可能会延迟,并且 您可以快速达到异步 Apex 方法的每日最大限制 执行。查看未来方法 性能最佳实践和 Lightning 平台 Apex限制。
- 方法的最大数量 每 24 小时的调用次数为 250,000 次,或者是 组织乘以 200,以较大者为准。此限制适用于您的整个 组织并与所有异步 Apex 共享:Batch Apex、Queueable Apex、scheduled Apex和未来的方法。检查有多少个异步 Apex 执行 ,则向 REST API 资源发出请求。查看清单 REST API 开发人员指南中的组织限制。计入的许可证类型 此限制包括完整的 Salesforce 和 Salesforce Platform 用户许可证、应用程序 订阅用户许可证、仅限 Chatter 用户、身份用户和公司 社区用户。futurelimits
注意
- 如果事务滚动,则不会处理事务排队的未来作业 返回。
- 在 Salesforce 服务维护停机之前排队的未来方法作业 留在队列中。服务停机结束后以及系统资源 变得可用,则将执行排队的未来方法作业。如果将来的方法 在发生停机时正在运行,将来的方法执行将回滚 并在服务恢复后重新启动。
测试未来的方法
若要测试使用注释定义的方法,请在 startTest() 和 stopTest() 代码块中调用包含该方法的类。全部异步 在该方法之后进行的调用是 由系统收集。什么时候是 执行时,所有异步进程都同步运行。futurestartTeststopTest对于我们的示例,下面是测试类。
@isTest
private class MixedDMLFutureTest {
@isTest static void test1() {
User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];
// System.runAs() allows mixed DML operations in test context
System.runAs(thisUser) {
// startTest/stopTest block to run future method synchronously
Test.startTest();
MixedDMLFuture.useFutureMethod();
Test.stopTest();
}
// The future method will run after Test.stopTest();
// Verify account is inserted
Account[] accts = [SELECT Id from Account WHERE Name='Acme'];
System.assertEquals(1, accts.size());
// Verify user is inserted
User[] users = [SELECT Id from User where username='mruiz@awcomputing.com'];
System.assertEquals(1, users.size());
}
}
未来方法性能最佳实践
Salesforce 使用基于队列的框架来处理 来自未来方法和批处理 Apex 等来源的异步过程。这 队列用于在组织之间平衡请求工作负载。使用 遵循最佳实践,以确保您的组织有效地使用队列 用于异步进程。