How to capture individual exceptions in bulk record updates
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
add a comment |
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
add a comment |
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
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
apex exception
asked Dec 10 at 15:08
Frank Evers
535
535
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
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.
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
add a comment |
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.
1
Exactly what I was looking for, much appreciated David!
– Frank Evers
Dec 10 at 15:50
add a comment |
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',
autoActivateHeartbeat: false,
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
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
add a comment |
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.
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
add a comment |
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.
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.
edited Dec 10 at 15:35
answered Dec 10 at 15:27
codeyinthecloud
3,149423
3,149423
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
add a comment |
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
add a comment |
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.
1
Exactly what I was looking for, much appreciated David!
– Frank Evers
Dec 10 at 15:50
add a comment |
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.
1
Exactly what I was looking for, much appreciated David!
– Frank Evers
Dec 10 at 15:50
add a comment |
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.
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.
answered Dec 10 at 15:27
David Reed
29.3k61746
29.3k61746
1
Exactly what I was looking for, much appreciated David!
– Frank Evers
Dec 10 at 15:50
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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