Mixed DML: Setup and Non-Setup Objects in the Same Transaction
“Inserted a User and an Account in the same transaction. Salesforce said absolutely not.”
What Happened
I was building an onboarding automation that created a new User and then created their Account record. Seemed logical. Salesforce threw MIXED_DML_OPERATION and I stared at the error for 20 minutes before Googling it. Turns out you can't perform DML on setup objects (User, PermissionSet, Group) and non-setup objects (Account, Contact) in the same transaction. This is one of those Salesforce-specific gotchas that no amount of general programming knowledge prepares you for.
The Wrong Way
// In a single method/transaction:
User u = new User(
LastName = 'Bradford',
Email = 'glen@cloudnimbus.com',
// ... required fields
);
insert u;
Account a = new Account(
Name = 'Cloud Nimbus LLC',
OwnerId = u.Id
);
insert a; // BOOM: MIXED_DML_OPERATIONThe Right Way
// Use @future or Queueable to separate the transactions
public class OnboardingService {
public static void createUserAndAccount(
String lastName, String email
) {
User u = new User(
LastName = lastName, Email = email
// ... required fields
);
insert u;
// Non-setup DML in a separate context
createAccountAsync(u.Id, lastName);
}
@future
private static void createAccountAsync(
Id userId, String name
) {
Account a = new Account(
Name = name, OwnerId = userId
);
insert a; // Different transaction. No conflict.
}
}The Lesson
Setup objects and non-setup objects cannot share a transaction. Use @future, Queueable, or Platform Events to separate them. Memorize which objects are 'setup' objects.
Enjoyed this? Get more like it.
Glen's Musings — AI, investing, and building things. Occasional. Free.
More Apex Mistakes
SOQL Query Inside a For Loop (The Classic)
Wrote a SOQL query inside a for loop. 200 records came in. 200 queries fired. Governor said no.
Read morePainfulTest Class That Only Tests One Record
Wrote a test class that inserted 1 record. Got 100% coverage. Deployed. Trigger failed on bulk operations.
Read morePainfulAll Logic in the Trigger Body (No Handler Class)
Wrote 300 lines of logic directly in the trigger. Six months later, nobody could maintain it. Including me.
Read more