Salesforce——DML operation on setup object is not permitted after you have updated a non-setup object

Salesforce——DML operations on sObjects results in MIXED_DML_OPERATION Error

  • 1、异常原因
    • 1.1、setup object
    • 1.2、non-setup object
  • 2、怎么解决
    • 2.1、@future
    • 2.2、Queueable Apex
    • 2.3、batch job

1、异常原因

翻译:在更新一个不是setup 对象的同时去操作一个setup对象是不被允许的
出现这样的原因是因为我在写代码的时候遇到这样的需求:我需要修改标准对象user上的一个去字段IsActive=true,因为只有满足这个条件的user才能在给他们创建Account和Opportunity这些对象,所以在我插入Account或者Opportunity之前我应该先去更改当前user的IsActive=true。
本来是个很简单的逻辑,只需在当前class里加上一小段代码然后update currentUser就行,但是异常却告诉我们这是一个混合操作。

Developer 社区:https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dml_non_mix_sobjects.htm
某些sObjects上的DML操作(有时称为setup对象)不能与同一事务中其他sObjects上的DML混合。存在此限制是因为某些sObjects会影响用户对组织中的记录的访问。您必须在不同的事务中插入或更新这些类型的sObjects,以防止使用不正确的访问级别权限进行操作。例如,您不能在单个事务中更新account、和User。

1.1、setup object

  • User
  • Profile
  • Layout

  • 简单来说这些对象都是会和 metadata 进行交互的

1.2、non-setup object

除去上面的对象,不管他是standard还是custom的都是non-setup object

2、怎么解决

既然不能在一个事务里,那我们就只能用asynchronous异步的方法来做了。

2.1、@future

该方法很简单,你需要做的就是在另一个类(不是在当前的类中写一个future方法哦)的一个方法中写另一个对象的DML操作。然后将该方法注解为@future

public class UtilHelper{
	@future
    public static void updateUser(Id userId){
    	//select user
    	//DML
    	//....
    } 
}

然后在你对acconut操作的那个类中调用该方法UtilHelper.updateUser(userId)
缺点

  • future方法只能接受原始类型的参数,比如String,Id,Integer等,你不能传入某个对象比如account或者user等,所以必须在方法里再根据Id查询一次。
  • 不能够被batch job或者被另一个future调用,即不能在一个异步事务里又调用异步的事务

2.2、Queueable Apex

Queueable相当于是future的一种扩展,它可以将一些有先后顺序执行的job构造成一个链:比如在插入account之前,必须将这个user更新为IsActive=true;并且可能被任何class调用

public class UpdateUser implements Queueable { 
    public void execute(QueueableContext context) { 
        // Awesome processing logic here    
        // Chain this job to next job by submitting the next job
        System.enqueueJob(new InsertAccount());
    }
}

2.3、batch job

global class SearchAndReplace implements Database.Batchable<sObject>{
   String query;//查询语句
   global SearchAndReplace(){
   		//构造查询语句
   }

   global Database.QueryLocator start(Database.BatchableContext BC){
   //查询对象
      return Database.getQueryLocator(query);
   }

   global void execute(Database.BatchableContext BC, List<sObject> scope){
     for(sobject s : scope){
     s.put(Field,Value); 
     }
     update scope;
    }

   global void finish(Database.BatchableContext BC){
   	//开启下一个batch job
   }
}

本篇文章不是重点将batch的,在这里只是表面是可以使用batch job来解决
Mix Operation的,只是代码量会明显大于另外两种方法。

PS:如有叙述不清或者错误的地方,恳请指正,留言交流,谢谢啦

你可能感兴趣的:(Apex,Execption)