+ Start a Discussion
Brian RomanowskiBrian Romanowski 

System.LimitException: Too many DML statements: 151 on Database.insert

I'm trying to utilize some code from the apex guide to share records. I'm getting the too many DML statements error when I run my bulk tests. The bulk test is just inserting a list of 200 objects. The code I modified is here:http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_bulk_sharing_creating_with_apex.htm

Error: System.LimitException: Too many DML statements: 151
Stack Trace: Class.IncidentSharing.manualShareRead: line 25, column 1
Trigger.IncidentTrigger: line 39, column 1

Line 25 column 1 is Database.SaveResult sr = Database.insert(incShr,false);

I realize the insert is running on every call but I don't know how to fix it. Should I pass a map to the class? Can I build a list inside the class and then insert it? I tried building a list and then inserting it but then how do I process the return? Thanks

Trigger Code 
FOR( BMCServiceDesk__Incident__c i : [SELECT id, BMCServiceDesk__Client_Manager__c FROM BMCServiceDesk__Incident__c WHERE (Equipment_Request__c= true OR Manager_Approval__c = true) AND Id IN: trigger.new ]) {
                IncidentSharing.manualShareRead(i.id,i.BMCServiceDesk__Client_Manager__c);
            }
Apes sharing class 
public class IncidentSharing {
   
   public static boolean manualShareRead(Id recordId, Id userOrGroupId){
   //public static boolean manualShareRead(map<Id,Id>){
      // Create new sharing object for the custom object Incident.
      BMCServiceDesk__incident__Share incShr  = new BMCServiceDesk__incident__Share();
       
      // Set the ID of record being shared.
      incShr.ParentId = recordId;
        
      // Set the ID of user or group being granted access.
      incShr.UserOrGroupId = userOrGroupId;
        
      // Set the access level.
      incShr.AccessLevel = 'Read';
        
      // Set rowCause to 'manual' for manual sharing.
      // This line can be omitted as 'manual' is the default value for sharing objects.
      incShr.RowCause = Schema.BMCServiceDesk__incident__Share.RowCause.Manual;
        
 
      // Insert the sharing record and capture the save result. 
      // The false parameter allows for partial processing if multiple records passed 
      // into the operation.
      Database.SaveResult sr = Database.insert(incShr,false);
      
 
      // Process the save results.
      if(sr.isSuccess()){
         //Indicates success
         return true;
      }
      else {
         // Get first save result error.
         Database.Error err = sr.getErrors()[0];
         
         // Check if the error is related to trival access level.
         // Access levels equal or more permissive than the object's default access level are not allowed. 
         // These sharing records are not required and thus an insert exception is acceptable. 
         if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION  &&  
                  err.getMessage().contains('AccessLevel')){
            // Indicates success.
            return true;
         }
         else{
            //Indicates failure.
            return false;
         }
         
		}
   }	
}


Best Answer chosen by Brian Romanowski
Gigi.OchoaGigi.Ochoa
You are performing a DML statement inside a loop, a big no no.

Try adding your share record to a list inside your loop, then perform your insert of the list outside the loop.

Example:

Trigger
List<BMCServiceDesk__Incident__c> iShareList = [[SELECT id, BMCServiceDesk__Client_Manager__c FROM BMCServiceDesk__Incident__c WHERE (Equipment_Request__c= true OR Manager_Approval__c = true) AND Id IN: trigger.new];

IncidentSharing.manualShareRead(iShareList);

Helper Class
public class IncidentSharing {
   
   public static boolean manualShareRead(List<BMCServiceDesk__Incident__c> iShareList){
   	
 	  // List of Shares to insert  	
   	  List<BMCServiceDesk__incident__Share> incShrList = new List<BMCServiceDesk__incident__Share >();
   	  
   	  for(BMCServiceDesk__Incident__c i:iShareList){   	  
	      // Create new sharing object for the custom object Incident.
	      BMCServiceDesk__incident__Share incShr  = new BMCServiceDesk__incident__Share();
	       
	      // Set the ID of record being shared.
	      incShr.ParentId = i.Id;
	        
	      // Set the ID of user or group being granted access.
	      incShr.UserOrGroupId = i.BMCServiceDesk__Client_Manager__c;
	        
	      // Set the access level.
	      incShr.AccessLevel = 'Read';
	        
	      // Set rowCause to 'manual' for manual sharing.
	      // This line can be omitted as 'manual' is the default value for sharing objects.
	      incShr.RowCause = Schema.BMCServiceDesk__incident__Share.RowCause.Manual;
	      
	      // Add share to list
	      incShrList.add(incShr);
   	  }
        
 
      // Insert the sharing record and capture the save result. 
      // The false parameter allows for partial processing if multiple records passed 
      // into the operation.
      Database.SaveResult[] sr = Database.insert(incShrList,false);
      
      // iterate through each returned result      
		for (Database.SaveResult sr : srList) {
		    if (sr.isSuccess()) {
		        // Operation was successful, so get the ID of the record that was processed
		        System.debug('Successfully inserted account. Account ID: ' + sr.getId());
		    }
		    else {
		        // Operation failed, so get all errors                
		        for(Database.Error err : sr.getErrors()) {
		            
		        }
		    }
		}
 
      
   }	
}




All Answers

Nilesh Jagtap (NJ)Nilesh Jagtap (NJ)
Hi Brian,

Looks like your trigger returning more than 150 records and for each record you are calling Database.Insert method which is throwing exception.

Please go through https://developer.salesforce.com/page/Apex_Code_Best_Practices for best practices.
Also, move DML class like(insert, update) otside for loop.

Thanks,
N.J
Gigi.OchoaGigi.Ochoa
You are performing a DML statement inside a loop, a big no no.

Try adding your share record to a list inside your loop, then perform your insert of the list outside the loop.

Example:

Trigger
List<BMCServiceDesk__Incident__c> iShareList = [[SELECT id, BMCServiceDesk__Client_Manager__c FROM BMCServiceDesk__Incident__c WHERE (Equipment_Request__c= true OR Manager_Approval__c = true) AND Id IN: trigger.new];

IncidentSharing.manualShareRead(iShareList);

Helper Class
public class IncidentSharing {
   
   public static boolean manualShareRead(List<BMCServiceDesk__Incident__c> iShareList){
   	
 	  // List of Shares to insert  	
   	  List<BMCServiceDesk__incident__Share> incShrList = new List<BMCServiceDesk__incident__Share >();
   	  
   	  for(BMCServiceDesk__Incident__c i:iShareList){   	  
	      // Create new sharing object for the custom object Incident.
	      BMCServiceDesk__incident__Share incShr  = new BMCServiceDesk__incident__Share();
	       
	      // Set the ID of record being shared.
	      incShr.ParentId = i.Id;
	        
	      // Set the ID of user or group being granted access.
	      incShr.UserOrGroupId = i.BMCServiceDesk__Client_Manager__c;
	        
	      // Set the access level.
	      incShr.AccessLevel = 'Read';
	        
	      // Set rowCause to 'manual' for manual sharing.
	      // This line can be omitted as 'manual' is the default value for sharing objects.
	      incShr.RowCause = Schema.BMCServiceDesk__incident__Share.RowCause.Manual;
	      
	      // Add share to list
	      incShrList.add(incShr);
   	  }
        
 
      // Insert the sharing record and capture the save result. 
      // The false parameter allows for partial processing if multiple records passed 
      // into the operation.
      Database.SaveResult[] sr = Database.insert(incShrList,false);
      
      // iterate through each returned result      
		for (Database.SaveResult sr : srList) {
		    if (sr.isSuccess()) {
		        // Operation was successful, so get the ID of the record that was processed
		        System.debug('Successfully inserted account. Account ID: ' + sr.getId());
		    }
		    else {
		        // Operation failed, so get all errors                
		        for(Database.Error err : sr.getErrors()) {
		            
		        }
		    }
		}
 
      
   }	
}




This was selected as the best answer
AshlekhAshlekh
Hi,

Your code is not following best parctises. 

For example : Your for loop calling a method and this method you are using DML operation. Assume your Trigger.new give you 151 record and then your mehod wil call 151 times and we know salesforce has limit on DML we can't exceute 151 call in one context.

 
public class IncidentSharing {
   
   public static BMCServiceDesk__incident__Share manualShareRead(Id recordId, Id userOrGroupId){
   //public static boolean manualShareRead(map<Id,Id>){
      // Create new sharing object for the custom object Incident.
      BMCServiceDesk__incident__Share incShr  = new BMCServiceDesk__incident__Share();
       
      // Set the ID of record being shared.
      incShr.ParentId = recordId;
        
      // Set the ID of user or group being granted access.
      incShr.UserOrGroupId = userOrGroupId;
        
      // Set the access level.
      incShr.AccessLevel = 'Read';
        
      // Set rowCause to 'manual' for manual sharing.
      // This line can be omitted as 'manual' is the default value for sharing objects.
      incShr.RowCause = Schema.BMCServiceDesk__incident__Share.RowCause.Manual;
	  
	  return incShr; 
        
	}
}

Trigger code will be
List<BMCServiceDesk__incident__Share> Sharelist = new List<BMCServiceDesk__incident__Share>();
FOR( BMCServiceDesk__Incident__c i : [SELECT id, BMCServiceDesk__Client_Manager__c FROM BMCServiceDesk__Incident__c WHERE (Equipment_Request__c= true OR Manager_Approval__c = true) AND Id IN: trigger.new ]) {
				BMCServiceDesk__incident__Share x = IncidentSharing.manualShareRead(i.id,i.BMCServiceDesk__Client_Manager__c);
				Sharelist.add(x);
}
Database.SaveResult[] s = Database.insert(Sharelist,false);
for( Database.SaveResult sr :s)
{
	if(sr.isSuccess()){
         //Indicates success
         return true;
      }
      else {
			// Get first save result error.
			 Database.Error err = sr.getErrors()[0];
         
			// Check if the error is related to trival access level.
			// Access levels equal or more permissive than the object's default access level are not allowed. 
			// These sharing records are not required and thus an insert exception is acceptable. 
			if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION  &&  
					err.getMessage().contains('AccessLevel')){
				// Indicates success.
				return true;
			}
			else{
				//Indicates failure.
				return false;
			}
	}
}

IF it helps you than please mark it as a solution and ENJOY APEX
Brian RomanowskiBrian Romanowski
Thanks! Gigi is the closest to what i'm looking for as I'm trying to move everything out of triggers and into classes. Problem is I get an error: Non-void method might not return a value or might have statement after a return statement. I did add returns to the save result loop. Same error with Ashlekh's code. 

This is the route I was headed down but I wasn't sure if it was a dead end.
Brian RomanowskiBrian Romanowski
Yeah I changed it. I created a boolean and set the value in each of the saveResult instances and then returned it. Thanks Everyone!
Gigi.OchoaGigi.Ochoa
You need to make sure that you will always return a boolean.  Likely there is a path that does not return a value.