rspec/rules/S5382/apex/rule.adoc

124 lines
3.5 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2021-04-28 16:49:39 +02:00
In order to protect shared resources, Salesforce enforces a maximum number of DML statements which can be executed inside a single https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_transaction.htm[transaction]. This is part of https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm[Governor limits]. If a DML statement is nested inside a loop's body (For/While/Do-While) it might be executed more times than the Governor limit allows, making the code fail.
Thus it is a best practice to not have DML statements nested in the body of loops and instead perform the DML operation on a list of sObjects.
This rule raises an issue when it detects DML statements inside a loop.
=== Noncompliant code example
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,apex]
2021-04-28 16:49:39 +02:00
----
public class myDMLLoop {
public static void myFunction() {
for (Task task: [Select Id, subject from Task]) {
if (task.subject == 'foo') {
task.subject = 'bar';
update task; // Noncompliant
}
}
}
}
// Query in a while loop
public class myDMLLoop {
public static void myFunction(Task[] tasks) {
Integer i = 0;
while ( i < 1000) {
Task task = tasks[i];
if (task.subject == 'foo') {
task.subject = 'bar';
update task; // Noncompliant
}
}
}
}
// Query in a do-while loop
public class myDMLLoop {
public static void myFunction(Task[] tasks) {
Integer i = 0;
do {
Task task = tasks[i];
if (task.subject == 'foo') {
task.subject = 'bar';
update task; // Noncompliant
}
i++;
} while (i < 1000);
}
}
----
=== Compliant solution
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,apex]
2021-04-28 16:49:39 +02:00
----
public class myDMLLoop {
public static void myFunction() {
List<Task> updatedTasks = new List<Task>();
for (Task task: [Select Id, subject from Task]) {
if (task.subject == 'foo') {
task.subject = 'bar';
updatedTasks.add(task);
}
}
update updatedTasks;
}
}
----
== Resources
2021-04-28 16:49:39 +02:00
* https://developer.salesforce.com/index.php?title=Apex_Code_Best_Practices&oldid=26951[Apex Code Best Practices]
* https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_loops_for_SOQL.htm[SOQL For Loops]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Move this DML statement out of the loop or process sObjects by batch.
'''
== Comments And Links
(visible only on this page)
=== on 5 Jul 2019, 17:35:38 Nicolas Harraudeau wrote:
If the rule raise false positives one possible exception is:
=== Exceptions
No issue will be raised when the DML statement is executed on a collection returned by a for loop. In this case the for loop already executed the DML statement in bulk.
----
public class myDMLLoop {
public static void myFunction() {
for (Task[] task: [Select Id, subject from Task]) { // Ok. The SOQL query is processing a batch of Tasks instead of a single one
task.subject = task.subject + ' processed';
update task; // This update a batch of Tasks
}
for (List<Task> tasks: [Select Id, subject from Task]) { // Same here
for (Task task: tasks) {
task.subject = task.subject + ' processed';
}
update tasks;
}
}
}
----
endif::env-github,rspecator-view[]