Mixed DML Operation

ליאור נכתב על ידי ליאור לביא, עודכן בתאריך 24/08/2021

הערה

פוסט זה מבוסס על התיעוד של Salesforce ומבקש להנגיש את התוכן המקורי בצורה ברורה ובעברית. למאמר המקורי מ-Salesforce לחץ כאן.

הודעת השגיאה

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

למה אנחנו רואים את הודעת השגיאה הזאת?

שגיאת Mix DML היא כנראה אחת השגיאות הראשונות בהן תתקל כמפתח Apex. הודעת השגיאה מציינת שאסור לנו ליצור או לעדכן ב-Database רשומות מסוג Setup ו-Non-Setup אחת אחרי השנייה, אלא שעל סמך ההודעה לא ברור מה הן רשומות מסוג Setup ו-Non-Setup או למה בכלל אסור להזין אותן ל-Database בו-זמנית.

ההגדרה Setup Object מתייחסת לכל אובייקט שמשפיע על הרשאות משתמשים ב-Salesforce. פרופילים, Roles ו-Permission Sets, לדוגמה, כולם Setup Objects. רשימת ה-Setup Objects ב-Salesforce נכון לכתיבת שורות אלו היא:

  • 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.
הערות ודגשים מיוחדים עבור חלק מהאובייקטים ניתן למצוא במאמר המקורי כאן.

ניסיון לבצע פעולת DML (שמירה, עדכון ומחיקה) על רשומות מאחד האובייקטים לעיל יחד עם אובייקטים שאינם ברשימה באותה הטרנסאקציה תגרור שגיאת Mixed DML Operation. מידע נוסף על טרנסאקציות ב-Salesforce תוכל לקרוא פה באתר בפוסט Salesforce Transactions.

או.קיי. אז הבנו מה הם Setup objects ו-Non-setup objects, אבל למה בעצם אי אפשר לבצע פעולות DML עליהם באותה הטרנסאקציה יחד? התשובה היא שאם נבצע שינוי להרשאות משתמשים על ידי עריכת רשומת Setup object יחד עם רשומות שאינן Setup object, ל-Salesforce לא תהיה דרך לדעת באיזו תצורת הרשאות להריץ את השינויים על האובייקטים שאינם Setup object. לדוגמה, בוא נגיד ואנחנו משנים למשתמש שמריץ את הטרנסאקציה את הפרופיל ובו-זמנית משנים Account כלשהו במערכת. מאחר ושתי הפעולות מבוצעות באותה הטרנסאקציה Salesforce לא יודעת תחת אילו הרשאות פועל המשתמש כשהוא מנסה לשנות את ה-Account, הרשאות הפרופיל הישן או החדש. הפתרון של Salesforce לבעיה הזאת הוא לאסור על ביצוע שינויים מסוג זה בו-זמנית.

פתרון

כדי לפתור את בעיית ה-Mixed DML שלנו נוכל להשתמש בשתי שיטות פשוטות:

  1. שימוש ב-Future Method - עובד בקוד סטנדרטי וב-Unit Tests.
  2. שימוש במתודה System.runAs - רלוונטי עבור Unit Tests בלבד.

שימוש ב-Future method למניעת שגיאת Mixed DML Operation

הרעיון מאחורי השימוש ב-Future method הוא ש-Future methods רצות ב-Execution context נפרד מאחר והן א-סינכרוניות. מידע נוסף על Future methods ב-Salesforce תוכל לקרוא פה באתר בפוסט Future Methods.

דוגמה

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;
	}
}

בדוגמה זאת פיצלנו את התהליך העסקי שלנו לשתי מתודות נפרדות, אחת רצה מידית ויוצרת Account חדש ב-Database ואז קוראת לשניה, myFutureClass.insertUser שרצה בעתיד, בצורה א-סינכרונית, ויוצרת משתמש חדש ב-Database.