What Is Mixed DML Operation in Salesforce

ליאור נכתב על ידי ליאור לביא, עודכן בתאריך 27/10/2023

Comment

This post is based on Salesforce documentation and aims to present the original content in a more clear manner. For the original Salesforce article, click here.

Error Message

MIXED_DML_OPERATION, DML operation on a setup object is not permitted after you have updated a non-setup object (or vice versa).

Why Are We Seeing This Error Message?

Mixed DML error is likely one of the first errors you'll encounter as an Apex developer. The error message indicates that you cannot create or update records of both Setup and Non-Setup objects in the same transaction. However, the message doesn't make it clear what Setup and Non-Setup objects are, or why it's prohibited to insert them into the database simultaneously.

The term "Setup Object" refers to any object that affects user permissions in Salesforce. Profiles, Roles, and Permission Sets, for example, are all considered Setup Objects. The list of Setup Objects in Salesforce, as of the time of writing, includes:

  • FieldPermissions
  • Group
  • GroupMember
  • ObjectPermissions
  • PermissionSet
  • PermissionSetAssignment
  • QueueSObject
  • ObjectTerritory2AssignmentRule
  • ObjectTerritory2AssignmentRuleItem
  • RuleTerritory2Association
  • SetupEntityAccess
  • Territory2
  • Territory2Model
  • UserTerritory2Association
  • User
  • UserRole
  • UserTerritory
  • Territory
  • Custom settings in Apex code saved using Salesforce API version 17.0 and earlier.

Specific notes and details about some of the objects can be found in the original article here.

Attempting to perform DML (insert, update, and delete) operations on records of one of the above-mentioned objects together with records that are not in the list in the same transaction will result in a Mixed DML Operation error. For more information on transactions in Salesforce, click here.

Okay, so we've understood what Setup objects and Non-Setup objects are, but why can't we perform DML operations on them in the same transaction? The answer is that if we make a change to user permissions by editing a Setup object record along with non-Setup object records, Salesforce wouldn't know under what permissions to run the changes on the non-Setup objects. For example, if we change a user's profile while also changing some Account in the system, since both operations are performed in the same transaction, Salesforce won't be able to determine which user permissions should apply when trying to make changes to the non-Setup object. As a result, Salesforce prohibits making such simultaneous changes.

Solution

To resolve our Mixed DML issue, we can use two simple methods:

  1. Using a future Method - Works in standard code and unit tests.
  2. Using the System.runAs() Method - Relevant for unit tests only.

Using a Future Method to Avoid Mixed DML Operation

The idea behind using a future method is that future methods run in a separate execution context asynchronously. For more information about future methods in Salesforce, click here.

Example

public class myClass {
    public static void mixedDMLOperation() {
        Account account = new Account(Name = 'Partanex');
        insert account;
        myFutureClass.insertUser(
            'partanexUserName@partanex.com',
            'parx',
            'partanexEmail@partanex.com',
            'Lavi'
        );
    }
}
public class myFutureClass {
    @future
    public static void insertUser(String username, String alias, String email, String lastName) {
        Profile profile = [SELECT Id FROM Profile WHERE Name = 'Standard User'];
        UserRole role = [SELECT Id FROM UserRole WHERE Name = 'COO'];
        User user = new User(
            Alias = alias,
            Email = email,
            Emailencodingkey = 'UTF-8',
            Lastname = lastname,
            Languagelocalekey = 'en_us',
            Localesidkey = 'en_us',
            profileId = profile.id,
            Userroleid = role.id,
            timezonesidkey = 'America/Los_angeles',
            Username = username
        );
        insert user;
    }
}

In this example, we split our business process into two separate methods. The first method runs immediately and creates a new account in the database, and then it calls the second method, myFutureClass.insertUser(), which runs in the future , asynchronously, creating a new user in the database.