Monday, April 10, 2017

How to avoid the MIXED_DML_OPERATION error

I recently came across the MIXED_DML_OPERATION error when writing a trigger to automate user access requests. The error was thrown when an update to a custom object triggered a process that updates the  users PermissionSetAssignment. Well as it turns out, DML operations on certain setup sObjects can’t be mixed with DML on other sObjects in the same transaction. In other words, if you're using a Visualforce page with a custom controller, you can't mix sObject types with any of these special sObjects within a single request or action. However, you can perform DML operations on these different types of sObjects in subsequent requests. For example, you can create an account with a save button, and then create a user with a non-null role with a submit button.

You can’t use the following sObjects with other sObjects when performing DML operations in the same transaction. 
  • FieldPermissions
  • GroupYou can only insert and update a group in a transaction with other sObjects. Other DML operations aren’t allowed.
  • GroupMemberYou can insert and update a group member only in a transaction with other sObjects in Apex code saved using Salesforce API version 14.0 and earlier.
  • ObjectPermissions
  • PermissionSet
  • PermissionSetAssignment
  • QueueSObject
  • ObjectTerritory2AssignmentRule
  • ObjectTerritory2AssignmentRuleItem
  • RuleTerritory2Association
  • SetupEntityAccess
  • Territory2
  • Territory2Model
  • UserTerritory2Association
  • User
So how do we get around this? 
  1. Create a method that performs a DML operation on one type of sObject.
  2. Create a second method that uses the future annotation to manipulate a second sObject type.

Here's an example of how to perform mixed DML operations by using a future method to perform a DML operation on the User object.
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(
'johndoe@acme.com', 'jdoe',
'johndoe@acme.com', 'DOE');
}
}
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;
}
}

Create a Lightning Component that Utilizes the Native Salesforce Global Search

I was recently working on a project where I needed to create Lightning Component that utilized the native global search to return items from...