Sunday 22 December 2013

Error: MIXED_DML_OPERATION


Few days back while writting an test class I encounterd an Error stating 

System.DmlException: Insert failed. First exception on row 0; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa):  

You will get this Error if you are trying to perform DML on setup and non-setup objects in same Transaction. 

Non-Setup objects can be any one of the Standard objects like Account or any Custom objcets,

Below are few examples of Setup objects

1.  FieldPermissions
2.  Group
You can only insert and update a group in transaction with   other SObject. Other DML operations are not allowed.
 
3.  GroupMember
You can only insert and update a group member in a transaction with other sObjects in Apex Code saved using Salesforce.com API version 14.0 and earlier.
 
4.  ObjectPermissions
5.  PermissionSet
6.  PermissionSetAssignment
7.  QueueSObject
8.  SetupEntityAccess
9.  User
  • You can insert a User in a transaction with other sObjects in  Apex code saved using Salesforce.com API version 14.0 and earlier.
  • You can insert a user in a transaction with other sObjects in Apex code saved using Salesforce.com API version 15.0 and later if UserRoleId is specified as null.
  • You can update a user in a transaction with other sObjects in Apex code saved using Salesforce.com API version 14.0 and earlier
  • You can update a user in a transaction with other sObjects in Apex code saved using Salesforce.com API version 15.0 and later if the following fields are not also updated:
    a. UserRoleId
    b. IsActive
    c. ForecastEnabled
    d. IsPortalEnabled
    e. Username
    f. ProfileId 


10. UserRole
11. UserTerritory
12. Custom Setting in Apex code saved using Salesforce.com API version 17.0 and earlier. 
 
Salesforce says:
DML operations on certain sObjects can’t be mixed with other sObjects in the same transaction. This is because some sObjects affect the user’s access to records in the organization. These types of sObjects must be inserted or updated in a different transaction to prevent operations from happening with incorrect access level permissions. For example, you can’t update an account and a user role in a single transaction. However, there are no restrictions on delete DML operations.



This error typically encountered in two types of Scenerio.
1. Non-Test Code
2. Test Code


1. Non-Test Code

Let us suppose I want to write a Trigger on Account Insert and this trigger will then create a Group and add current User as member to that newly created Group.


 NOTE : Version of Trigger is 29.0

Now when tried to create a new Account You would get an error 



[ BEFORE MOVING FURTHER LETS JUST WAIT FOR A SEC, AS TRIGGER VERSION IS SET To 29, LET SEE WHAT WILL HAPPEN IF VERSION OF TRIGGER IS CHANGED TO VERSION 14, AND REMOVE "doesIncludeBosses " attribute from Account Trigger WHILE CREATING GROUP AS IT WILL THROW AN ERROR. NOW SAVE TRIGGER AND TRY TO CREATE ACCOUN, YOU WON'T GET THE SAME ERROR. REFER SETUP OBJECT LIST ABOVE POINT 3(GroupMember)  ]

SOLUTION IS Simple, we can split the DML into future and non future context i.e.
1. Perform DML on Non-Setup object type
2. Perform DML on Setup object type in @future methods.

or vice-versa
 
AccountInsertTrigger.trigger
 


 AccountHandler.cls




2. Test-Code

Now lets take a scenerio where you have test above Trigger

while executing this test class we will receive Error


This problem can be fixed by starting new context in the future method.
Do these changes in AccountHandler.cls
 


now run the test class

this will work for both UI and Test code



References :




1 comment:

  1. This was very helpful. Thanks Puneet for such a informative blog.

    ReplyDelete