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
aKallNVaKallNV 

Mixed DML error on Trigger that is only inserting and updating Contact object

I have found some info out there about Mixed_DML errors, but most of them have to do with Controllers and action chaining for VF pages. 

 

I am getting one on a trigger that attempts to do the following:

 

Up until this point every employee gets a seat in our SFDC org. We also make a Contact record for every employee to record cases, training etc. Therefore, I wanted to make a trigger that created a Contact whenever a new User record is created or upates the Contact whenever the corresponding User is edited. So the following is what I started with. The logic isn't complete, but there is enough there to get started. Anyway. it's giving me the following when I try to test by creating a new user in the interface.

 

MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): Contact, original object: User: []

 

What I'm confused by(take warning here, I'm a completely self-taught novice and struggling) is that the docs say that mixed dml errors occur because a single method is trying to manipulate two different sObjects, and that the solution is to have the two dml statements in two different methods one of which is tagged with @future. However, I am only trying to run DML on the Contact object, so what's the deal?

 

BTW, I'm totally open to people making suggestions on how to make this code more efficient, elegant etc.

 

 

Thanks!

 

 

trigger userTrigger on User (after insert, after update) {

	List<Contact> newCons = new List<Contact>();
	List<Contact> exCons = new List<Contact>();
	
	if(trigger.isInsert) {
		for(User newU : trigger.new) {
			Contact theCon = new Contact();
			theCon.FirstName = newU.FirstName;
			theCon.LastName = newU.LastName;
			theCon.Email = newU.Email;
			theCon.Department =newU.Department__c;
			theCon.Unit__c = newU.Unit__c;
			theCon.StartDate__c = newU.DateofHire__c;
			theCon.EndDate__c = newU.TerminationDate__c;
			theCon.Title = newU.Title;
			theCon.Phone = newU.Phone;
			theCon.AccountId = '001Q000000G7LHh';
			
			newCons.Add(theCon);
			
		}		
	}
	
	insert newCons;
	
	if(trigger.isUpdate) {
		List<Contact> existingCons = [select ID, FirstName, LastName, Email, Department, Unit__c, StartDate__c, EndDate__c, Title, Phone from Contact where Id IN :trigger.newMap.keySet()];
		for(Contact existingCon : existingCons) {
			if(existingCon.FirstName != trigger.newMap.get(existingCon.Id).FirstName) {
				existingCon.FirstName = trigger.newMap.get(existingCon.Id).FirstName;
			}
			if(existingCon.LastName != trigger.newMap.get(existingCon.Id).LastName) {
				existingCon.LastName = trigger.newMap.get(existingCon.Id).LastName;
			}
			if(existingCon.Email != trigger.newMap.get(existingCon.Id).Email) {
				existingCon.Email = trigger.newMap.get(existingCon.Id).Email;
			}
			if(existingCon.Department != trigger.newMap.get(existingCon.Id).Department__c) {
				existingCon.Department = trigger.newMap.get(existingCon.Id).Department__c;
			}
			if(existingCon.Unit__c != trigger.newMap.get(existingCon.Id).Unit__c) {
				existingCon.Unit__c = trigger.newMap.get(existingCon.Id).Unit__c;
			}
			if(existingCon.StartDate__c != trigger.newMap.get(existingCon.Id).DateofHire__c) {
				existingCon.StartDate__c = trigger.newMap.get(existingCon.Id).DateofHire__c;
			}
			if(existingCon.EndDate__c != trigger.newMap.get(existingCon.Id).TerminationDate__c) {
				existingCon.EndDate__c = trigger.newMap.get(existingCon.Id).TerminationDate__c;
			}
			if(existingCon.Title != trigger.newMap.get(existingCon.Id).Title) {
				existingCon.Title = trigger.newMap.get(existingCon.Id).Title;				
			}
			if(existingCon.phone != trigger.newMap.get(existingCon.Id).phone) {
				existingCon.phone = trigger.newMap.get(existingCon.Id).phone;
			}
			
			//newCons.add(existingCon);
		}
	}
	
	update exCons;
}

 

 

bob_buzzardbob_buzzard

While you aren't making any changes to a setup object, the transaction that you are in has already inserted/updated a user record so I think you are hitting problems due to that.

aKallNVaKallNV

Thanks Bob. 

 

I have broken up the insert and update statements into two methods and have tagged both with @future. This got the insert working well, but opened up other problems with the update. I am going to struggle with it on my own for a bit, but may post again if I give up.

 

 

jonathanrico.jonathanrico.

I'm doing something similar but I only get the MIXED DML exception on After Insert. After Update seems to work fine.

aKallNVaKallNV

Hey,

 I did this a while ago, so not sure what the solution was but this code is working for me.

 

public with sharing class userHandler {
    
    //this is the method that is triggered by a User Insert, After Insert
    @future
    public static void createCons(Set<ID> newUs) {
        
        List<User> theNewUs = [select ID, FirstName, LastName,MobilePhone, Email, Department__c, Unit__c,CFN__c, DateofHire__c, TerminationDate__c, Title, Phone from User where ID IN: newUs];
        List<Contact> newCons = new List<Contact>();
        List<User> userUpdate = new List<User>();
        
        for(User newU : theNewUs) {
            Contact theCon = new Contact();
            theCon.FirstName = newU.FirstName;
            theCon.LastName = newU.LastName;
            theCon.Email = newU.Email;
            theCon.Department =newU.Department__c;
            theCon.Unit__c = newU.Unit__c;
            theCon.StartDate__c = newU.DateofHire__c;
            theCon.EndDate__c = newU.TerminationDate__c;
            theCon.Title = newU.Title;
            theCon.Phone = newU.Phone;
            theCon.MobilePhone = newU.MobilePhone;
            theCon.CFN__c = newU.CFN__c;
            if(newU.CFN__c) {
                theCon.AccountId = '0014000000JEsYEAA1';
            }else {
                theCon.AccountID = '014000000IzgeNAAR';
                
            }
            theCon.UserId__c = newU.Id;
            theCon.RecordTypeId = '0124000000015bTAAQ';
                        
            newCons.add(theCon);            
        }       
        
        insert newCons;
        
    }
    
    @future
    public static void updateCons(Set<ID> existingUs) {
        
        List<Contact> consUpdate = new List<Contact>();
        Map<String,User> idToUser = new Map<String,User>([select ID, FirstName, LastName, Email, ManagerId, Manager.ContactID__c, Department__c, Unit__c, CFN__c, DateofHire__c, TerminationDate__c, Title, Phone, MobilePhone from User where ID IN: existingUs]);
        
                
        for(Contact existingCon : [select ID, FirstName, LastName, Email, ReportsToId, Department, Unit__c,CFN__c, StartDate__c, EndDate__c, Title, Phone, MobilePhone, UserID__c from Contact where userID__c IN :idToUser.keySet()]) {
            
            if(existingCon.FirstName != idToUser.get(existingCon.UserID__c).FirstName) {
                existingCon.FirstName = idToUser.get(existingCon.UserID__c).FirstName;
            }
            if(existingCon.LastName != idToUser.get(existingCon.UserID__c).LastName) {
                existingCon.LastName = idToUser.get(existingCon.UserID__c).LastName;
            }
            if(existingCon.Email != idToUser.get(existingCon.UserID__c).Email) {
                existingCon.Email = idToUser.get(existingCon.UserID__c).Email;
            }
            if(existingCon.Department != idToUser.get(existingCon.UserID__c).Department__c) {
                existingCon.Department = idToUser.get(existingCon.UserID__c).Department__c;
            }
            if(existingCon.Unit__c != idToUser.get(existingCon.UserID__c).Unit__c) {
                existingCon.Unit__c = idToUser.get(existingCon.UserID__c).Unit__c;
            }
            if(existingCon.StartDate__c != idToUser.get(existingCon.UserID__c).DateofHire__c) {
                existingCon.StartDate__c = idToUser.get(existingCon.UserID__c).DateofHire__c;
            }
            if(existingCon.EndDate__c != idToUser.get(existingCon.UserID__c).TerminationDate__c) {
                existingCon.EndDate__c = idToUser.get(existingCon.UserID__c).TerminationDate__c;
            }
            if(existingCon.Title != idToUser.get(existingCon.UserID__c).Title) {
                existingCon.Title = idToUser.get(existingCon.UserID__c).Title;              
            }
            if(existingCon.MobilePhone != idToUser.get(existingCon.UserID__c).MobilePhone) {
                existingCon.MobilePhone = idToUser.get(existingCon.UserID__c).MobilePhone;              
            }
            if(existingCon.phone != idToUser.get(existingCon.UserID__c).phone) {
                existingCon.phone = idToUser.get(existingCon.UserID__c).phone;
            }
            if(existingCon.CFN__c != idToUser.get(existingCon.UserID__c).CFN__c) {
                existingCon.CFN__c = idToUser.get(existingCon.UserID__c).CFN__c;                
            }
            if(null != idToUser.get(existingCon.UserID__c).Manager.ContactID__c || (existingCon.ReportsToId != idToUser.get(existingCon.UserID__c).Manager.ContactID__c)) {
                existingCon.ReportsToId = idToUser.get(existingCon.UserID__c).Manager.ContactID__c;
            }
            
            consUpdate.add(existingCon);
        }
        
        update consUpdate;
        
    }
}

 

jonathanrico.jonathanrico.

Yup, that should work fine since it's being executed in a future context.

 

I'm working on a functionality to sync a User with a Contact , all of this process is handled with triggers and it seems that the save behavior for a User is different when created through the web interface since I only get MIXED_DML exceptions when creating users through the UI.

 

What I ended up doing is that I look for a MIXED_DML exception in my trigger , if this happens I do a  second attempt to insert/update  contacts but in a future method. I did it this way because I wan't to use future calls the least I can, I don't have a lot of full salesforce/force.com licenses in my ORG and I don't want to hit a governor limit for daily future calls.

 

...

 

Sorry but I can't resist to warn you of those hardcoded IDs in your code, always got to watch out on those otherwise you're going to have a lot of fun when deploying this.

aKallNVaKallNV

Point taken on your advice about hard coding. I just didn't feel like writing soql statements to pull two record types, and two accounts.  The code is live, with fingers crossed.

 

anyway, I just wanted to clarify that these methods are being called by a trigger...so my project is similar to yours in that we're both trying to sync users and contacts. I also experienced some weird behavior with mixed dml statements. In my case the error would throw when I ran tests in SFDC testing envirnonment, but the errors were not thrown when I ran the tests in eclipse. This was confusing so I tried validating a deployment in Eclipse and no errors were thrown. 

jonathanrico.jonathanrico.

Were you getting the MIXED_DML exceptions only when running tests through the web interface or you just had different test coverage %?

 

There's a weird bug with test coverage % when running tests through the web.

aKallNVaKallNV

No I had 0% coverage with mixed dml messages in the interface. My coverage was fine in eclipse. And, this sounds really stupid now, but I deployed via Eclipse and now that the code is in production it is throwing the same error when I run test through production user interface.