How to capture individual exceptions in bulk record updates











up vote
2
down vote

favorite












Today I am writing an apex class to transform a bulk set of data from one record to another. During the course of this transfer, it is expected that we will have errors on the records in the system. What we would like to do if an error is found, is mark the record as error, and add the exception that caused the error to field on the record. We would then like the class to continue to process records.



What I am unable to figure out at this time, is how do I capture the exception of the record in error, and toss it into our error bucket, without stopping the process of the remainder of the records or hitting any limits?



My first thought was to insert all the records into a list, and then go through each record attempting an insert in a try catch block. I then realized this would still be in danger of hitting our SOQL limits as we would have to do individual inserts for every record in the list. On the other hand, if I attempt to do an insert of the entire list at once in a try catch block, we will not be able to capture the error on the relevant record that I can think of, and the processing will stop upon the failure of any record.



With those two ideas both out, I am struggling to come up with a way to design this system to fulfill both of the needs outlined above. I am hoping that perhaps someone with a little better understanding of the exception system can provide me an alternative design that can store individual exceptions on the records that caused them without stopping processing the remainder or risking running into limits.










share|improve this question


























    up vote
    2
    down vote

    favorite












    Today I am writing an apex class to transform a bulk set of data from one record to another. During the course of this transfer, it is expected that we will have errors on the records in the system. What we would like to do if an error is found, is mark the record as error, and add the exception that caused the error to field on the record. We would then like the class to continue to process records.



    What I am unable to figure out at this time, is how do I capture the exception of the record in error, and toss it into our error bucket, without stopping the process of the remainder of the records or hitting any limits?



    My first thought was to insert all the records into a list, and then go through each record attempting an insert in a try catch block. I then realized this would still be in danger of hitting our SOQL limits as we would have to do individual inserts for every record in the list. On the other hand, if I attempt to do an insert of the entire list at once in a try catch block, we will not be able to capture the error on the relevant record that I can think of, and the processing will stop upon the failure of any record.



    With those two ideas both out, I am struggling to come up with a way to design this system to fulfill both of the needs outlined above. I am hoping that perhaps someone with a little better understanding of the exception system can provide me an alternative design that can store individual exceptions on the records that caused them without stopping processing the remainder or risking running into limits.










    share|improve this question
























      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      Today I am writing an apex class to transform a bulk set of data from one record to another. During the course of this transfer, it is expected that we will have errors on the records in the system. What we would like to do if an error is found, is mark the record as error, and add the exception that caused the error to field on the record. We would then like the class to continue to process records.



      What I am unable to figure out at this time, is how do I capture the exception of the record in error, and toss it into our error bucket, without stopping the process of the remainder of the records or hitting any limits?



      My first thought was to insert all the records into a list, and then go through each record attempting an insert in a try catch block. I then realized this would still be in danger of hitting our SOQL limits as we would have to do individual inserts for every record in the list. On the other hand, if I attempt to do an insert of the entire list at once in a try catch block, we will not be able to capture the error on the relevant record that I can think of, and the processing will stop upon the failure of any record.



      With those two ideas both out, I am struggling to come up with a way to design this system to fulfill both of the needs outlined above. I am hoping that perhaps someone with a little better understanding of the exception system can provide me an alternative design that can store individual exceptions on the records that caused them without stopping processing the remainder or risking running into limits.










      share|improve this question













      Today I am writing an apex class to transform a bulk set of data from one record to another. During the course of this transfer, it is expected that we will have errors on the records in the system. What we would like to do if an error is found, is mark the record as error, and add the exception that caused the error to field on the record. We would then like the class to continue to process records.



      What I am unable to figure out at this time, is how do I capture the exception of the record in error, and toss it into our error bucket, without stopping the process of the remainder of the records or hitting any limits?



      My first thought was to insert all the records into a list, and then go through each record attempting an insert in a try catch block. I then realized this would still be in danger of hitting our SOQL limits as we would have to do individual inserts for every record in the list. On the other hand, if I attempt to do an insert of the entire list at once in a try catch block, we will not be able to capture the error on the relevant record that I can think of, and the processing will stop upon the failure of any record.



      With those two ideas both out, I am struggling to come up with a way to design this system to fulfill both of the needs outlined above. I am hoping that perhaps someone with a little better understanding of the exception system can provide me an alternative design that can store individual exceptions on the records that caused them without stopping processing the remainder or risking running into limits.







      apex exception






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Dec 10 at 15:08









      Frank Evers

      535




      535






















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          3
          down vote



          accepted










          To Begin with what you're looking for is a partial insert instead of all or nothing and be able to report back on individual record level errors or exceptions.



          Database.insert(recordsToInsert, allOrNone): Adds one or more sObjects, such as individual accounts or contacts, to your organization’s data. You can achieve partial inserts using this above method and specifying allorNone as FALSE.



          recordsToInsert
          Type: sObject



          allOrNone
          Type: Boolean



          The optional allOrNone parameter specifies whether the operation allows partial success. If you specify false for this parameter and a record fails, the remainder of the DML operation can still succeed. This method returns a result object that can be used to verify which records succeeded, which failed, and why. If the parameter is not set or is set true, an exception is thrown if the method is not successful.



          Exceptions in Apex
          Exceptions note errors and other events that disrupt the normal flow of code execution. throw statements are used to generate exceptions, while try, catch, and finally statements are used to gracefully recover from exceptions.



          There are many ways to handle errors in your code, including using assertions like System.assert calls, or returning error codes or Boolean values, so why use exceptions? The advantage of using exceptions is that they simplify error handling. Exceptions bubble up from the called method to the caller, as many levels as necessary, until a catch statement is found to handle the error. This bubbling up relieves you from writing error handling code in each of your methods. Also, by using finally statements, you have one place to recover from exceptions, like resetting variables and deleting data. Also read more about List of all possible APEX Exceptions



          SaveResult Class An array of SaveResult objects is returned with the insert and update database methods. Each element in the SaveResult array corresponds to the sObject array passed as the sObject parameter in the Database method, that is, the first element in the SaveResult array matches the first element passed in the sObject array, the second element corresponds with the second element, and so on. If only one sObject is passed in, the SaveResult array contains a single element.



          The following example shows how to obtain and iterate through the returned Database.SaveResult objects. It inserts two accounts using Database.insert with a false second parameter to allow partial processing of records on failure. One of the accounts is missing the Name required field, which causes a failure. Next, it iterates through the results to determine whether the operation was successful or not for each record. It writes the ID of every record that was processed successfully to the debug log, or error messages and fields of the failed records. This example generates one successful operation and one failure.



          // Create two accounts, one of which is missing a required field
          Account accts = new List<Account>{
          new Account(Name='Account1'),
          new Account()};
          Database.SaveResult srList = Database.insert(accts, 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()) {
          System.debug('The following error has occurred.');
          System.debug(err.getStatusCode() + ': ' + err.getMessage());
          System.debug('Account fields that affected this error: ' + err.getFields());
          }
          }
          }



          Now looking at the above example what you should be doing is use
          database.insert to allow partial inserts and leverage saveresult class
          to report any errors back with some positive exception handling. Do
          remember that exceptions caused by governer limts cannot be caught
          and is going to disrupt your flow.







          share|improve this answer



















          • 1




            This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
            – Frank Evers
            Dec 10 at 15:52










          • @FrankEvers Your welcome. Glad I was able to help!
            – codeyinthecloud
            Dec 10 at 16:04


















          up vote
          6
          down vote













          Briefly, what you need to do is switch from using update DML statements to Database.update() methods, because the latter allow you to specify allOrNone=false. Doing so means that the method will always complete (no DML exception or transaction rollback) and will return errors to you for inspection and handling.



          There's useful details at DML Statements vs. Database Class Methods.



          In your batch class, what you might do is something like this:



          void execute(Database.BatchableContext bc, List<Account> scope) {
          for (Account a : scope) {
          // Mutate `a` here.
          }

          List<Database.SaveResult> srs = Database.update(scope, false);
          // `false` is the `allOrNone` option

          List<Account> errorsToUpdate = new List<Account>();
          for (Database.SaveResult sr : srs) {
          if (!sr.isSuccess()) {
          String error = '';
          for (Database.Error err : sr.getErrors()) {
          error += String.valueOf(err.getStatusCode()) + ': ' + err.getMessage() + 'n');
          }
          errorsToUpdate.add(new Account(Id = sr.getId(), My_Error__c = error));
          }
          }

          // Persist error details, throwing an exception if the DML fails.
          update errorsToUpdate;
          }


          Essentially, you perform your error-prone operation, requesting error details be returned to you rather than an exception being thrown. Then, you process those error details into a further bulk update to your records. You may wish, as I've shown here, for that update to fail with an exception, rolling back the transaction if you're unable to persist the error details.






          share|improve this answer

















          • 1




            Exactly what I was looking for, much appreciated David!
            – Frank Evers
            Dec 10 at 15:50











          Your Answer








          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "459"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsalesforce.stackexchange.com%2fquestions%2f241989%2fhow-to-capture-individual-exceptions-in-bulk-record-updates%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          3
          down vote



          accepted










          To Begin with what you're looking for is a partial insert instead of all or nothing and be able to report back on individual record level errors or exceptions.



          Database.insert(recordsToInsert, allOrNone): Adds one or more sObjects, such as individual accounts or contacts, to your organization’s data. You can achieve partial inserts using this above method and specifying allorNone as FALSE.



          recordsToInsert
          Type: sObject



          allOrNone
          Type: Boolean



          The optional allOrNone parameter specifies whether the operation allows partial success. If you specify false for this parameter and a record fails, the remainder of the DML operation can still succeed. This method returns a result object that can be used to verify which records succeeded, which failed, and why. If the parameter is not set or is set true, an exception is thrown if the method is not successful.



          Exceptions in Apex
          Exceptions note errors and other events that disrupt the normal flow of code execution. throw statements are used to generate exceptions, while try, catch, and finally statements are used to gracefully recover from exceptions.



          There are many ways to handle errors in your code, including using assertions like System.assert calls, or returning error codes or Boolean values, so why use exceptions? The advantage of using exceptions is that they simplify error handling. Exceptions bubble up from the called method to the caller, as many levels as necessary, until a catch statement is found to handle the error. This bubbling up relieves you from writing error handling code in each of your methods. Also, by using finally statements, you have one place to recover from exceptions, like resetting variables and deleting data. Also read more about List of all possible APEX Exceptions



          SaveResult Class An array of SaveResult objects is returned with the insert and update database methods. Each element in the SaveResult array corresponds to the sObject array passed as the sObject parameter in the Database method, that is, the first element in the SaveResult array matches the first element passed in the sObject array, the second element corresponds with the second element, and so on. If only one sObject is passed in, the SaveResult array contains a single element.



          The following example shows how to obtain and iterate through the returned Database.SaveResult objects. It inserts two accounts using Database.insert with a false second parameter to allow partial processing of records on failure. One of the accounts is missing the Name required field, which causes a failure. Next, it iterates through the results to determine whether the operation was successful or not for each record. It writes the ID of every record that was processed successfully to the debug log, or error messages and fields of the failed records. This example generates one successful operation and one failure.



          // Create two accounts, one of which is missing a required field
          Account accts = new List<Account>{
          new Account(Name='Account1'),
          new Account()};
          Database.SaveResult srList = Database.insert(accts, 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()) {
          System.debug('The following error has occurred.');
          System.debug(err.getStatusCode() + ': ' + err.getMessage());
          System.debug('Account fields that affected this error: ' + err.getFields());
          }
          }
          }



          Now looking at the above example what you should be doing is use
          database.insert to allow partial inserts and leverage saveresult class
          to report any errors back with some positive exception handling. Do
          remember that exceptions caused by governer limts cannot be caught
          and is going to disrupt your flow.







          share|improve this answer



















          • 1




            This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
            – Frank Evers
            Dec 10 at 15:52










          • @FrankEvers Your welcome. Glad I was able to help!
            – codeyinthecloud
            Dec 10 at 16:04















          up vote
          3
          down vote



          accepted










          To Begin with what you're looking for is a partial insert instead of all or nothing and be able to report back on individual record level errors or exceptions.



          Database.insert(recordsToInsert, allOrNone): Adds one or more sObjects, such as individual accounts or contacts, to your organization’s data. You can achieve partial inserts using this above method and specifying allorNone as FALSE.



          recordsToInsert
          Type: sObject



          allOrNone
          Type: Boolean



          The optional allOrNone parameter specifies whether the operation allows partial success. If you specify false for this parameter and a record fails, the remainder of the DML operation can still succeed. This method returns a result object that can be used to verify which records succeeded, which failed, and why. If the parameter is not set or is set true, an exception is thrown if the method is not successful.



          Exceptions in Apex
          Exceptions note errors and other events that disrupt the normal flow of code execution. throw statements are used to generate exceptions, while try, catch, and finally statements are used to gracefully recover from exceptions.



          There are many ways to handle errors in your code, including using assertions like System.assert calls, or returning error codes or Boolean values, so why use exceptions? The advantage of using exceptions is that they simplify error handling. Exceptions bubble up from the called method to the caller, as many levels as necessary, until a catch statement is found to handle the error. This bubbling up relieves you from writing error handling code in each of your methods. Also, by using finally statements, you have one place to recover from exceptions, like resetting variables and deleting data. Also read more about List of all possible APEX Exceptions



          SaveResult Class An array of SaveResult objects is returned with the insert and update database methods. Each element in the SaveResult array corresponds to the sObject array passed as the sObject parameter in the Database method, that is, the first element in the SaveResult array matches the first element passed in the sObject array, the second element corresponds with the second element, and so on. If only one sObject is passed in, the SaveResult array contains a single element.



          The following example shows how to obtain and iterate through the returned Database.SaveResult objects. It inserts two accounts using Database.insert with a false second parameter to allow partial processing of records on failure. One of the accounts is missing the Name required field, which causes a failure. Next, it iterates through the results to determine whether the operation was successful or not for each record. It writes the ID of every record that was processed successfully to the debug log, or error messages and fields of the failed records. This example generates one successful operation and one failure.



          // Create two accounts, one of which is missing a required field
          Account accts = new List<Account>{
          new Account(Name='Account1'),
          new Account()};
          Database.SaveResult srList = Database.insert(accts, 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()) {
          System.debug('The following error has occurred.');
          System.debug(err.getStatusCode() + ': ' + err.getMessage());
          System.debug('Account fields that affected this error: ' + err.getFields());
          }
          }
          }



          Now looking at the above example what you should be doing is use
          database.insert to allow partial inserts and leverage saveresult class
          to report any errors back with some positive exception handling. Do
          remember that exceptions caused by governer limts cannot be caught
          and is going to disrupt your flow.







          share|improve this answer



















          • 1




            This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
            – Frank Evers
            Dec 10 at 15:52










          • @FrankEvers Your welcome. Glad I was able to help!
            – codeyinthecloud
            Dec 10 at 16:04













          up vote
          3
          down vote



          accepted







          up vote
          3
          down vote



          accepted






          To Begin with what you're looking for is a partial insert instead of all or nothing and be able to report back on individual record level errors or exceptions.



          Database.insert(recordsToInsert, allOrNone): Adds one or more sObjects, such as individual accounts or contacts, to your organization’s data. You can achieve partial inserts using this above method and specifying allorNone as FALSE.



          recordsToInsert
          Type: sObject



          allOrNone
          Type: Boolean



          The optional allOrNone parameter specifies whether the operation allows partial success. If you specify false for this parameter and a record fails, the remainder of the DML operation can still succeed. This method returns a result object that can be used to verify which records succeeded, which failed, and why. If the parameter is not set or is set true, an exception is thrown if the method is not successful.



          Exceptions in Apex
          Exceptions note errors and other events that disrupt the normal flow of code execution. throw statements are used to generate exceptions, while try, catch, and finally statements are used to gracefully recover from exceptions.



          There are many ways to handle errors in your code, including using assertions like System.assert calls, or returning error codes or Boolean values, so why use exceptions? The advantage of using exceptions is that they simplify error handling. Exceptions bubble up from the called method to the caller, as many levels as necessary, until a catch statement is found to handle the error. This bubbling up relieves you from writing error handling code in each of your methods. Also, by using finally statements, you have one place to recover from exceptions, like resetting variables and deleting data. Also read more about List of all possible APEX Exceptions



          SaveResult Class An array of SaveResult objects is returned with the insert and update database methods. Each element in the SaveResult array corresponds to the sObject array passed as the sObject parameter in the Database method, that is, the first element in the SaveResult array matches the first element passed in the sObject array, the second element corresponds with the second element, and so on. If only one sObject is passed in, the SaveResult array contains a single element.



          The following example shows how to obtain and iterate through the returned Database.SaveResult objects. It inserts two accounts using Database.insert with a false second parameter to allow partial processing of records on failure. One of the accounts is missing the Name required field, which causes a failure. Next, it iterates through the results to determine whether the operation was successful or not for each record. It writes the ID of every record that was processed successfully to the debug log, or error messages and fields of the failed records. This example generates one successful operation and one failure.



          // Create two accounts, one of which is missing a required field
          Account accts = new List<Account>{
          new Account(Name='Account1'),
          new Account()};
          Database.SaveResult srList = Database.insert(accts, 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()) {
          System.debug('The following error has occurred.');
          System.debug(err.getStatusCode() + ': ' + err.getMessage());
          System.debug('Account fields that affected this error: ' + err.getFields());
          }
          }
          }



          Now looking at the above example what you should be doing is use
          database.insert to allow partial inserts and leverage saveresult class
          to report any errors back with some positive exception handling. Do
          remember that exceptions caused by governer limts cannot be caught
          and is going to disrupt your flow.







          share|improve this answer














          To Begin with what you're looking for is a partial insert instead of all or nothing and be able to report back on individual record level errors or exceptions.



          Database.insert(recordsToInsert, allOrNone): Adds one or more sObjects, such as individual accounts or contacts, to your organization’s data. You can achieve partial inserts using this above method and specifying allorNone as FALSE.



          recordsToInsert
          Type: sObject



          allOrNone
          Type: Boolean



          The optional allOrNone parameter specifies whether the operation allows partial success. If you specify false for this parameter and a record fails, the remainder of the DML operation can still succeed. This method returns a result object that can be used to verify which records succeeded, which failed, and why. If the parameter is not set or is set true, an exception is thrown if the method is not successful.



          Exceptions in Apex
          Exceptions note errors and other events that disrupt the normal flow of code execution. throw statements are used to generate exceptions, while try, catch, and finally statements are used to gracefully recover from exceptions.



          There are many ways to handle errors in your code, including using assertions like System.assert calls, or returning error codes or Boolean values, so why use exceptions? The advantage of using exceptions is that they simplify error handling. Exceptions bubble up from the called method to the caller, as many levels as necessary, until a catch statement is found to handle the error. This bubbling up relieves you from writing error handling code in each of your methods. Also, by using finally statements, you have one place to recover from exceptions, like resetting variables and deleting data. Also read more about List of all possible APEX Exceptions



          SaveResult Class An array of SaveResult objects is returned with the insert and update database methods. Each element in the SaveResult array corresponds to the sObject array passed as the sObject parameter in the Database method, that is, the first element in the SaveResult array matches the first element passed in the sObject array, the second element corresponds with the second element, and so on. If only one sObject is passed in, the SaveResult array contains a single element.



          The following example shows how to obtain and iterate through the returned Database.SaveResult objects. It inserts two accounts using Database.insert with a false second parameter to allow partial processing of records on failure. One of the accounts is missing the Name required field, which causes a failure. Next, it iterates through the results to determine whether the operation was successful or not for each record. It writes the ID of every record that was processed successfully to the debug log, or error messages and fields of the failed records. This example generates one successful operation and one failure.



          // Create two accounts, one of which is missing a required field
          Account accts = new List<Account>{
          new Account(Name='Account1'),
          new Account()};
          Database.SaveResult srList = Database.insert(accts, 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()) {
          System.debug('The following error has occurred.');
          System.debug(err.getStatusCode() + ': ' + err.getMessage());
          System.debug('Account fields that affected this error: ' + err.getFields());
          }
          }
          }



          Now looking at the above example what you should be doing is use
          database.insert to allow partial inserts and leverage saveresult class
          to report any errors back with some positive exception handling. Do
          remember that exceptions caused by governer limts cannot be caught
          and is going to disrupt your flow.








          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Dec 10 at 15:35

























          answered Dec 10 at 15:27









          codeyinthecloud

          2,879422




          2,879422








          • 1




            This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
            – Frank Evers
            Dec 10 at 15:52










          • @FrankEvers Your welcome. Glad I was able to help!
            – codeyinthecloud
            Dec 10 at 16:04














          • 1




            This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
            – Frank Evers
            Dec 10 at 15:52










          • @FrankEvers Your welcome. Glad I was able to help!
            – codeyinthecloud
            Dec 10 at 16:04








          1




          1




          This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
          – Frank Evers
          Dec 10 at 15:52




          This is hugely informative and should solve my issue perfectly! Thank you for taking the time to write all of this up.
          – Frank Evers
          Dec 10 at 15:52












          @FrankEvers Your welcome. Glad I was able to help!
          – codeyinthecloud
          Dec 10 at 16:04




          @FrankEvers Your welcome. Glad I was able to help!
          – codeyinthecloud
          Dec 10 at 16:04












          up vote
          6
          down vote













          Briefly, what you need to do is switch from using update DML statements to Database.update() methods, because the latter allow you to specify allOrNone=false. Doing so means that the method will always complete (no DML exception or transaction rollback) and will return errors to you for inspection and handling.



          There's useful details at DML Statements vs. Database Class Methods.



          In your batch class, what you might do is something like this:



          void execute(Database.BatchableContext bc, List<Account> scope) {
          for (Account a : scope) {
          // Mutate `a` here.
          }

          List<Database.SaveResult> srs = Database.update(scope, false);
          // `false` is the `allOrNone` option

          List<Account> errorsToUpdate = new List<Account>();
          for (Database.SaveResult sr : srs) {
          if (!sr.isSuccess()) {
          String error = '';
          for (Database.Error err : sr.getErrors()) {
          error += String.valueOf(err.getStatusCode()) + ': ' + err.getMessage() + 'n');
          }
          errorsToUpdate.add(new Account(Id = sr.getId(), My_Error__c = error));
          }
          }

          // Persist error details, throwing an exception if the DML fails.
          update errorsToUpdate;
          }


          Essentially, you perform your error-prone operation, requesting error details be returned to you rather than an exception being thrown. Then, you process those error details into a further bulk update to your records. You may wish, as I've shown here, for that update to fail with an exception, rolling back the transaction if you're unable to persist the error details.






          share|improve this answer

















          • 1




            Exactly what I was looking for, much appreciated David!
            – Frank Evers
            Dec 10 at 15:50















          up vote
          6
          down vote













          Briefly, what you need to do is switch from using update DML statements to Database.update() methods, because the latter allow you to specify allOrNone=false. Doing so means that the method will always complete (no DML exception or transaction rollback) and will return errors to you for inspection and handling.



          There's useful details at DML Statements vs. Database Class Methods.



          In your batch class, what you might do is something like this:



          void execute(Database.BatchableContext bc, List<Account> scope) {
          for (Account a : scope) {
          // Mutate `a` here.
          }

          List<Database.SaveResult> srs = Database.update(scope, false);
          // `false` is the `allOrNone` option

          List<Account> errorsToUpdate = new List<Account>();
          for (Database.SaveResult sr : srs) {
          if (!sr.isSuccess()) {
          String error = '';
          for (Database.Error err : sr.getErrors()) {
          error += String.valueOf(err.getStatusCode()) + ': ' + err.getMessage() + 'n');
          }
          errorsToUpdate.add(new Account(Id = sr.getId(), My_Error__c = error));
          }
          }

          // Persist error details, throwing an exception if the DML fails.
          update errorsToUpdate;
          }


          Essentially, you perform your error-prone operation, requesting error details be returned to you rather than an exception being thrown. Then, you process those error details into a further bulk update to your records. You may wish, as I've shown here, for that update to fail with an exception, rolling back the transaction if you're unable to persist the error details.






          share|improve this answer

















          • 1




            Exactly what I was looking for, much appreciated David!
            – Frank Evers
            Dec 10 at 15:50













          up vote
          6
          down vote










          up vote
          6
          down vote









          Briefly, what you need to do is switch from using update DML statements to Database.update() methods, because the latter allow you to specify allOrNone=false. Doing so means that the method will always complete (no DML exception or transaction rollback) and will return errors to you for inspection and handling.



          There's useful details at DML Statements vs. Database Class Methods.



          In your batch class, what you might do is something like this:



          void execute(Database.BatchableContext bc, List<Account> scope) {
          for (Account a : scope) {
          // Mutate `a` here.
          }

          List<Database.SaveResult> srs = Database.update(scope, false);
          // `false` is the `allOrNone` option

          List<Account> errorsToUpdate = new List<Account>();
          for (Database.SaveResult sr : srs) {
          if (!sr.isSuccess()) {
          String error = '';
          for (Database.Error err : sr.getErrors()) {
          error += String.valueOf(err.getStatusCode()) + ': ' + err.getMessage() + 'n');
          }
          errorsToUpdate.add(new Account(Id = sr.getId(), My_Error__c = error));
          }
          }

          // Persist error details, throwing an exception if the DML fails.
          update errorsToUpdate;
          }


          Essentially, you perform your error-prone operation, requesting error details be returned to you rather than an exception being thrown. Then, you process those error details into a further bulk update to your records. You may wish, as I've shown here, for that update to fail with an exception, rolling back the transaction if you're unable to persist the error details.






          share|improve this answer












          Briefly, what you need to do is switch from using update DML statements to Database.update() methods, because the latter allow you to specify allOrNone=false. Doing so means that the method will always complete (no DML exception or transaction rollback) and will return errors to you for inspection and handling.



          There's useful details at DML Statements vs. Database Class Methods.



          In your batch class, what you might do is something like this:



          void execute(Database.BatchableContext bc, List<Account> scope) {
          for (Account a : scope) {
          // Mutate `a` here.
          }

          List<Database.SaveResult> srs = Database.update(scope, false);
          // `false` is the `allOrNone` option

          List<Account> errorsToUpdate = new List<Account>();
          for (Database.SaveResult sr : srs) {
          if (!sr.isSuccess()) {
          String error = '';
          for (Database.Error err : sr.getErrors()) {
          error += String.valueOf(err.getStatusCode()) + ': ' + err.getMessage() + 'n');
          }
          errorsToUpdate.add(new Account(Id = sr.getId(), My_Error__c = error));
          }
          }

          // Persist error details, throwing an exception if the DML fails.
          update errorsToUpdate;
          }


          Essentially, you perform your error-prone operation, requesting error details be returned to you rather than an exception being thrown. Then, you process those error details into a further bulk update to your records. You may wish, as I've shown here, for that update to fail with an exception, rolling back the transaction if you're unable to persist the error details.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Dec 10 at 15:27









          David Reed

          28.6k61746




          28.6k61746








          • 1




            Exactly what I was looking for, much appreciated David!
            – Frank Evers
            Dec 10 at 15:50














          • 1




            Exactly what I was looking for, much appreciated David!
            – Frank Evers
            Dec 10 at 15:50








          1




          1




          Exactly what I was looking for, much appreciated David!
          – Frank Evers
          Dec 10 at 15:50




          Exactly what I was looking for, much appreciated David!
          – Frank Evers
          Dec 10 at 15:50


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Salesforce Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsalesforce.stackexchange.com%2fquestions%2f241989%2fhow-to-capture-individual-exceptions-in-bulk-record-updates%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Сан-Квентин

          8-я гвардейская общевойсковая армия

          Алькесар