function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
HarmpieHarmpie 

Receiving error MIXED_DML_OPERATION on User Trigger

I have the following Trigger, which I want to use to update ownership of 2 custom objects (Business_Unit__c & Sub_Unit__c) when a user role changes. The role and it's corresponding BU / Sub BU are fetched and should be updated with this new UserId as the owner.
 
Code:
trigger RoleUpdateBuOwner on User (after update) {
 
 Map<ID,User> trUsersNew = Trigger.newMap;
 Map<ID,User> trUsersOld = Trigger.oldMap;
 
 Set<String> allUpdatedRoles = new Set<String>(); 
 
 Map<Id,Business_Unit__c> allBu = new Map<Id,Business_Unit__c>([SELECT Id,Name,OwnerId FROM Business_Unit__c]);
 Map<Id,Sub_Unit__c> allSubBu = new Map<Id,Sub_Unit__c>([SELECT Id,Name,OwnerId FROM Sub_Unit__c]);
 
 Integer numBuUpdated = 0;
 List<Business_Unit__c> buToUpdate = new List<Business_Unit__c>();
 for(Business_Unit__c aBu : allBu.values()) {
  for(User userWithNewRole : trUsersNew.values()) {
   if(userWithNewRole.Id != aBu.OwnerId) {
    if(userWithNewRole.Role_Name__c.contains(aBu.Name)  && userWithNewRole.Role_Name__c.contains('Plant Manager')) {
     aBu.OwnerId = userWithNewRole.Id; 
     buToUpdate.add(aBu);
     numBuUpdated++;
    }
   }  
  }
 }
  
 Integer numSubBuUpdated = 0;
 List<Sub_Unit__c> subBuToUpdate = new List<Sub_Unit__c>();
 for(Sub_Unit__c aSubBu : allSubBu.values()) {
  for(User userWithNewRole : trUsersNew.values()) {
   if(userWithNewRole.UserRoleId == null) continue;
   if(userWithNewRole.Id != aSubBu.OwnerId) {
    if(userWithNewRole.Role_Name__c.contains(aSubBu.Name) && userWithNewRole.Role_Name__c.contains('Plant Manager')) {
     aSubBu.OwnerId = userWithNewRole.Id; 
     subBuToUpdate.add(aSubBu);
     numSubBuUpdated++;
    }
   }  
  }
 }
 update subBuToUpdate;
 update buToUpdate;
 
 /*
 // For testing
 for(User thisUser : Trigger.new) {
  thisUser.addError('Updating '+numBuUpdated+' BU and '+numSubBuUpdated+' Sub BU');
 }
 */
 
}

 
Now, when I update a user role, the following error appears:
 
Code:
Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger RoleUpdateBuOwner caused an unexpected exception, contact your administrator: RoleUpdateBuOwner: execution of AfterUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id a0FR00000007OWzMAM; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object: DML operation on non-setup object is not supported after you have updated a setup object: Business_Unit__c: [Id]: Trigger.RoleUpdateBuOwner: line 40, column 5

 
What is wrong with my trigger (that causes this error:))?


Message Edited by Harmpie on 12-12-2008 05:35 AM
JimRaeJimRae
I don't know why that occurs, but it appears you will need to make this a before trigger instead of after,
that way you would update your 2 custom objects first, then the update of the user record would occur.
HarmpieHarmpie
Thanks for the response JimRae. I modified the trigger to a before update (no other changes), but I now get almost the same error:
 
Code:
Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger RoleUpdateBuOwner caused an unexpected exception, contact your administrator: RoleUpdateBuOwner: execution of BeforeUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id a0FR00000007OWzMAM; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object: DML operation on non-setup object is not supported after you have updated a setup object: Business_Unit__c: [Id]: Trigger.RoleUpdateBuOwner: line 40, column 5

 
HarmpieHarmpie
Bump... Can someone please tell me if it's possible in the first place what I am trying here? I.e. trigger on User object, updating related objects
JimRaeJimRae
According to the Apex Reference guide (page 171) you cannot do any DML operation on a User and other sObjects in the same transaction, for example, with a trigger.  It lists several other objects that have this limitation as well.
  • Group
  • GroupMember
  • QueueSObject
  • User
  • UserRole
  • UserTerritory
  • Territory
The documentation indicates the workaround for this is to:

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.




HarmpieHarmpie
Thanks a lot for helping me out, missed it in the manual myself. At least there's a work-around for it too.
AssafAssaf
Hi Harmpie,

Can you elaborate what you did in order to solve it?
I'm afraid I didn't understand the offered solution above.

thanks, Assaf.
odieodie

Hi,

 

I was working on a similar problem. In my case I commented out all the code in the Trigger except for a comment and the trigger still does not fire. I also tried making the trigger a "before" trigger; that did not work too. Whats with triggers on the User object?

 

 

trigger usernameChange on User (after update) { System.debug('fired!'); for (User uNew : Trigger.new) { //for(User uOld : Trigger.old){ // if(uOld.id == uNew.id){ // System.debug('found match!'); // if(uOld.username != uNew.username) // System.debug('Changed!'); //} //} } }

 

 

 

werewolfwerewolf
Well are you sure the trigger's actually not firing?  Is the trigger active?  The problem you're noting here (odie's I mean) is quite different from the mixed DML issue this thread was originally about.  The fix, though, using a @future method, works.
odieodie

Hi,

 

Sorry about the "hijack". To answer your questions:

1) yes the trigger is active

2) and yes the trigger does something - (System.debug())

 

I also tried making the Apex version to 10; the trigger still does not fire.

Oddly, I find this trigger on the user object marked as covered in the test results page when I run unit tests for triggers on the Lead and Contact objects. Anybody seen such behaviour?

 

thanks,

Odie. 

osamanosaman

There is a work around for that.

If you want to perform two DML operations sequentially, create a seprate method for 2nd DML operation with @future token.

make sure you put System.RunAs() in the method. Your overall method should look like this

 

private static void myFunc()

{

     ///1st DML operation

 

     User usr = [Select id from User where Id = :UserInfo.getUserId()];

 

     System.RunAs(usr)

     {

        Test.startTest();

         myFunc2();

         Test.stopTest();

     }

 

 

}

 

@future

private static void myFunc2()

{

   ///2nd DML operation

}