You need to sign in to do that
Don't have an account?
try catch block question
I have a before insert and update trigger that updates the account.ownerid based on criteria that is met by matching the account.billing state to zone.state__c custom object. I created a try catch block to catch and exception and throw the error to a custom errorlog object. But it doesn't seem to catch it. Is this because a query exception is not considered a dml exception? alsoi have A method called getNumDMl(), does this method count all exceptions or just dml ones? I want to basically catch as many exceptions as possible and throw to error log. any advice would be great
catch (QueryException e)
{
Error_Log__c eLog = new Error_Log__c();
List<Error_Log__c> eLogList = new List<Error_Log__c>();
for (Integer iex = 0; iex < e.getNumDml(); iex++)
{
eLog.Description__c = e.getDmlMessage(iex);
eLogList.add(eLog);
}
insert eLogList;
}
sfdcls:
Glad we were able to help
1. In 'before' triggers, the try-catch block is 'generally' there to catch exceptions in your apex code logic (null pointers, array index, etc.). As you now know, it won't catch errors that occur during validation rules or downstream triggers that fire as a result of workflow field updates
2. I would recommend putting more data into the error message including exception type and line number so when that call to IT comes, you have something to go by without requiring the user to repeat the test with the debug log turned on. I would also review your validation rules to make sure they are clear and 'actionable' to the end user as those are the ones they are most likely to see.
3. You simply can't count failed rows in the exception catch block because the 'before' trigger try-catch isn't catching DML exceptions on your SObject X. For a typical end user scenario, they are only updating one row - the record they are 'saving'.
Bulk trigger idiom is there to handle data loader, API, web service, or other operations (such as VF controllers or 'after' triggers on different Sobjects doing updates/inserts on 1 or more of your Sobject X) - And in any of these cases, it is the caller that invokes the SObject X trigger that needs to wrap that update/insert DML operation with try-catch - and then, if the exception is DmlException, you have the # of rows.
When the Sobject X trigger is invoked via a SAVE on the end user's Force.com UI, it is SFDC itself that has the try-catch surrounding the 'Save' - and its action is to rollback all updates and display an error message to the end user
All Answers
A queryexception is reserved for when you have a
malformed query (such as "SELECT Foo FORM Bar" ( should be "FROM" ) using a dynamic queryproblem with query-variable mismatches (e.g. using a non-list variable and trying to assign muliple rows). You'll be wanting a DmlException instead. getNumDML() wil return just the failed rows, not all rows. If you want to catch ANY exception, catch Exception (the parent of all exceptions).ok looks like I need to use the general exception called 'Exception'.
For this, could I use getNumDML() as the method to count the # exceptions? I couldn't find a general exception counter for this. Any idea what i could use the counter in my for statement to catch everything.
catch (Exception e)
{
Error_Log__c eLog = new Error_Log__c();
List<Error_Log__c> eLogList = new List<Error_Log__c>();
for (Integer iex = 0; iex < e.getNumDml(); iex++)
{
eLog.Description__c = e.getDmlMessage(iex);
eLogList.add(eLog);
}
insert eLogList;
}
An alternative that I regularly use is rather than do DML with insert or update statements, I use the Database.insert or Database.update methods.
1> They allow for partial successes
2> You get back a list of Database.saveResult objects that in turn tell you if row[i] was a success, and if not, what the error was on that row[i]
Much more fine-grained control over what actually happened in a bulk DML operation
I see the benefit for database methods and with partial processing. What I'm struggling with is that this is a before insert /update trigger so I'm trying catch the Exceptions. The insert dml on the error log is processing records that were already caught as an exception so how would I incorpoate this database method for this scenari?. Especially a batch insert, I want to be able to catch exceptions for each row.
Database.SaveResult SR = database.insert(eLogList);
for(Database.Error err: SR.getErrors())
{
// throw error to the error log here?
}
sfdcls:
A few principles at work here for before update/insert triggers
1. If the trigger is on some Sobject X, then you update the fields on X simply by changing values in the Trigger.new list that is part of the Trigger context. No DML statements needed.
2. If the trigger is on some SObject X and you want to update a different SObject Y - this is best done in After update/insert triggers and you can use either:
* Database.insert or Database.update methods
* insert or update statements
3. If you are using Database.xxx methods, you wouldn't normally use try-catch around them to catch DML errors (rather, the try-catch would be to catch null pointer exceptions) as the Database.xxx methods give you programmatic control over each row's insert/update status
4. If you want to detect error conditions on SObject X in the before/after trigger for X, then use if statements and the SObject method addError -- this will tell SFDC that the relevant row in the Trigger.new list is in error and should not be committed. The addError method has the same logical effect as a validation rule - just that it occurs before validation rules because before triggers execute ahead of validation rules.
Thank you so much this. This is very helpful. My situation would apply to #1 so trigger.new passes it to the method in my class. Although #4 makes sense, instead of throwing an error to the user with .addError method, the requirement is to exit out of the trigger and insert the exception into the error_log__c. What is the best way for me to loop through any type of exception that is caught? I origially had this code in an after insert/update and worked well using getNumDml to catch dmlException but i want use a general exception handler (Exception) to catch all exceptions. this is where i am stuck. Maybe I need to remove the try/catch block all together and use an if statement to catch the error?
My 2nd issue is trying to re-create an error so i can pass coverage.
public with sharing class AccountUtilities
{
public static void ZoneAssign(List<Account> aList)
{
try
{
List<Zone__c> tList = [select id, zip_start__c, zip_end__c, state__c,User__c from Zone__c];
for(Account a: aList)
{
for(integer i= 0; i< tList.size(); i++)
{
if(a.BillingPostalCode>= tList.get(i).zip_start__c && a.BillingPostalCode<=tList.get(i).zip_end__c )
{
a.Zone_c = tList.get(i).id;
a.OwnerId = tList.get(i).User__c;
}
}
}
}
}
catch (dmlException e)
{
// just temporary until i figure this out
System.debug('There was an error!');
}
/* Error_Log__c eLog = new Error_Log__c();
List<Error_Log__c> eLogList = new List<Error_Log__c>();
for (Integer iex = 0; iex < getNumDml ; iex++)
{
eLog.Description__c = e.getDmlMessage(iex);
eLogList.add(eLog);
}
insert eLogList;
} */
}
}
Let's deal with these in turn
#1 - I'm a bit puzzled -- you want the user to be able to insert/update a record and if there are errors, not be told that there are errors? Assuming I have this wrong...
* A Trigger is going to implicitly update/insert the changed fields in Trigger.new unless you have called addError on that SObj instance.
* Even after the trigger executes, validation rules will fire on X and they could cause a rollback. The trigger can't catch this case as the trigger has lost context
* A VF controller can override the Save method and you can do the insert/update explicitly with a Database.insert or Database.update and detect all errors, including validation rule errors. You can then save those errors to your error log SObject and tell the user that the update/insert failed using ApexPages.addMessage(). Of course, the VF controller only operates from the UI - either on a single SObject or a set of SObjects (such as a mass edit from a list). It will not be invoked for data loader or API calls.
#2 - To force exceptions
Here is what I do --
1. I have a static class with some public booleans like forceNullPointer = false;
2. In my production code, I test for the value of MyStatic.Class.forceNullPointer. If true, then I execute code that causes a null pointer exception
3. In my testmethod, I set the value of MyStaticClass.forceNullPointer = true in order to trip the exception
For testing DML errors, a similar technique can be used - for example, by making a required field null before the DML operation
thank you for reply. What I meant for #1 is that if there is an exception from this trigger, instead of the user being notified the exception is inserted as a record into the error_log__c object. Then a standard workflow notification will be sent to the IT team to review the exception in the error_log. From what I read, the .addError method stops the transaction so if I display a message to the end user such as "You have recieved an error, and IT team will get back to you" would I still be able to catch the exception and insert it into the error log? Looks like I can do this with a VF controller but we're not doing any UI modifications. I also had this same try catch block in an after insert trigger which explcitly had a dml statement and it caught the dmlException but i can't seem to catch the exceptions on a before insert/update. also, i will check the static class on null-pointer to force an exception. If you have example of the static class that would be great.
If you call so much as a single addError, your Error_Log__c record will be toast, so you can't both display a friendly error to the user AND log the event (at least, not including the debug logs).
Edit: I mean, you can use addError, but it has to be caught before it reaches the system level (e.g. is caught by a try-catch block somewhere). You still can't directly report the error to the user, though. You could have a custom field, and use a VF page that displays the error message, if any. I've seen the likes for places where it would be advisable to have a warning instead of an actual error (e.g. a transaction failed).
sfdcfox (who is very wise) is correct
I go back to the previous suggestion -- if your application requirement can be met by a VF controller, then you can control all the DML by overriding the SAVE action; you can also log. Validation rule errors can be caught.
yeah just trying figure our how to catch any type of exceptions throw into the log. is anythuing wrong with my catch block?? im trying reporudce an error and nothing works
catch (Exception e)
{
System.debug('There was an error!');
}
/* Error_Log__c eLog = new Error_Log__c();
List<Error_Log__c> eLogList = new List<Error_Log__c>();
for (Integer iex = 0; iex < 2; iex++)
{
eLog.Description__c = e.getDmlMessage(iex);
eLogList.add(eLog);
}
insert eLogList;
} */
}
}
sfdcls:
Your catch block per se is fine catch(Exception e) {..} will catch any exception thrown within the associated try{..} block.
However, as stated earlier, it will not catch update/insert errors due to validation rules or downstream triggers/workflow field updates/more validation rules/triggers. Those can not be caught in a before trigger as those DML events occur after the trigger script code has ended.
The code you have commented out has conceptual issues:
crop1645, after fully digesting all the posts and reading up on order of execution in salesforce, i can see why I can't catch these errors AND why they can't be inserted into the error log. However, I can't do a vf page right now since it's beyond scope. So i am left with the conclusion that the only way to handle exception right now is to use the .addError method and alert the user. The reason why I am placing an exception handler is as you guys know, it's a suggested best practice so it's not like I have to insert into the error log as long i have a handler to catch the issue. I am going to fix the logic using .addError and catch all exceptions in 1 catch block and display something like 'There has been an unexpected error, please contact IT 1800-111-1111';
2 questions:
1. As long as I handle these exceptions this way, am i still within best practices for before insert/update exception handling? assuming the vf page can't be build to way later since they specifically don';t want any custom UI
2. If i catch a general exception ---- //catch (Exception e) , I don;t see a method to count # of failed rows other than getNumDml(). is there one i can use for Exception since this is not a handler specifically to catch dmlException.
You guys have been awesome - great learning experience so far
fyi - i know i need to update my code below and will mark this as a solutions so others can see it and give credit to u guys but just showing the latest
catch (Exception e)
{
Error_Log__c eLog = new Error_Log__c();
List<Error_Log__c> eLogList = new List<Error_Log__c>();
for (Integer iex = 0; iex < e.getNumDml(); iex++)
{
eLog.Description__c = e.getDmlMessage(iex);
eLogList.add(eLog);
}
insert eLogList;
}
}
}
sfdcls:
Glad we were able to help
1. In 'before' triggers, the try-catch block is 'generally' there to catch exceptions in your apex code logic (null pointers, array index, etc.). As you now know, it won't catch errors that occur during validation rules or downstream triggers that fire as a result of workflow field updates
2. I would recommend putting more data into the error message including exception type and line number so when that call to IT comes, you have something to go by without requiring the user to repeat the test with the debug log turned on. I would also review your validation rules to make sure they are clear and 'actionable' to the end user as those are the ones they are most likely to see.
3. You simply can't count failed rows in the exception catch block because the 'before' trigger try-catch isn't catching DML exceptions on your SObject X. For a typical end user scenario, they are only updating one row - the record they are 'saving'.
Bulk trigger idiom is there to handle data loader, API, web service, or other operations (such as VF controllers or 'after' triggers on different Sobjects doing updates/inserts on 1 or more of your Sobject X) - And in any of these cases, it is the caller that invokes the SObject X trigger that needs to wrap that update/insert DML operation with try-catch - and then, if the exception is DmlException, you have the # of rows.
When the Sobject X trigger is invoked via a SAVE on the end user's Force.com UI, it is SFDC itself that has the try-catch surrounding the 'Save' - and its action is to rollback all updates and display an error message to the end user
crop1645 - thank a million for the last post. I got my exception handler to work. I ended up creating multiple try catch blocks. 2 of which I know ill occur at the business logic level - queryException and nullPointerException. I plan on building a VF page to catch errors caught on validation rules or when triggers fire again as result of workflow..for now, I placed a general exception block at the end to catch anything else at business logic level and the insertion into the error log worked for nullPointer. Also notice instead of getNumDml, the loop ends since I referenced the List<Account> aList which are the records passed from trigger.new. any issues you see here for this one? I'm probably going to get rid of the logic of throwing the error into the error_log and instead use the .addError method like the first 2 and suggest monitoring the users in the debug log. i think this will work now...the @future async operation to create the error in the log is an interesting concept but looks like it's catch 22 based on the other thread. what do you think so far?i think this should work until the vf page is built later...what do u think? now i need to finish up the test class
catch (System.NullPointerException e)
{
for(Account a: aList)
{
a.addError('There was an error with the record since a field was not populated correctly, please contact IT at 1888-888-8888 for asssitance, error number ' + e.getLineNumber());
}
}
catch (System.queryException e)
{
for(Account a: aList)
{
a.addError('There was an error with the record since the system recognize zero or multiple Territory records, please contact IT at 1888-888-8888 for assistance, error number ' + e.getLineNumber());
}
}
catch(Exception e)
{
System.debug('There was an unexpected exception - see log details below');
Error_Log__c eLog = new Error_Log__c();
List<Error_Log__c> eLogList = new List<Error_Log__c>();
for (Integer iex = 0; iex < aList.size(); iex++)
{
eLog.Description__c = 'Error line number: ' + e.getLineNumber() + 'Error Message ' + e.getMessage();
eLogList.add(eLog);
}
insert eLogList;
}
}
sfdcls:
Without knowing the code in the try block; it is quite likely that you will know the specific Account where the error occurred and you won't need to reject the whole batch - just the record (Account) in error. That is, call addError only on the SObject in error. This way the Data Loader and other batch invokers of your trigger can handle partial batch successes (DataLoader implicitly will).
For this to work, the variable bound to the 'account in error' will need to be declared outside the try{} block so as to be available to the catch {} block.
Since you are obviously concerned about a robust reporting system, may I also suggest adopting an error code naming system for your addError statements. For example:
a.addError('[A-01] There was an error with the record since a field was not populated correctly, please contact IT at 1888-888-8888 for assistance' + e.getLineNumber()); and
a.addError(''[A-02] There was an error with the record since the system recognize zero or multiple Territory records, please contact IT at 1888-888-8888 for assistance' + e.getLineNumber());
This makes it easy to find the spot in the code where the error occurred. I do this also with Validation Rules.
As for the @future, I'm convinced that they won't execute if SFDC is going to rollback the transaction - which SFDC will do on an addError.
Eric- you saved me from making a huge mistake. I forgot about the inability to handle partial successes during a batch load in your previous post. I wouldn;t have know that until i did a batch test in my test class. basically, the code in my try block is not going to work. I'll declare a var above the try statement so it;s within the scope of the catch block. Thank you also for recommending the addError convention- that makes way more sense - I will update that as well. Here is my try block. Im going to update the code now but let me know if you see any other issues. Also, i had to change the territory custom object to zone__c
public static void ZoneAssign(List<Account> aList)
{
try
{
List<Zone__c> zList = [select id,SalesRep__c, recordTypeId from Zone__c];
Map<Id,RecordType> rtMap = new Map<Id,RecordType>([Select id,Name from RecordType where sObjectType IN ('Account','Zone__c')]);
for(Account a: aList)
{
for(integer i= 0; i< tList.size(); i++)
{
if(rtMap.get(a.recordtypeid).Name == rtMap.get(tList.get(i).recordtypeid).Name)
{
a.Zone__c = zList.get(i).id;
a.OwnerId = zList.get(i).Zone__c;
}
}
}
}
Hey Eric- so I was going to use the database methods to handle partial processing but for before insert/update, dml and database methods won't work right? I saw in your previous post that I could use if statements - is there an example some where on wiki I could digest? thinking...ummmm...
also, I found this on the wiki but i think this will only work on an after insert/update
1
Database.SaveResult[] lsr = Database.update(accounts, false);
2
3
for(Database.SaveResult sr : lsr){
4
if (!sr.isSuccess()) {
5
myMethodToaddToErrorObjectList(sr);
6
}
7
}
sfdcls:
Before triggers
* You can't/shouldn't use Database.xxx methods on the triggered SObject X.
* You can mark only those triggered SObjects in error with addError; any triggered SObjects without addError will commit - provided the trigger invoker is designed to handle partial updates - Data Loader, for example, is designed to handle partial updates - so is inline View edits. Any other custom client that invokes your triggers has to be designed to handle partial updates -- if written in APEX, by using the Database.xxx methods
After triggers
* In order to do any DML, an after trigger must explicitly use a DML statement like 'update' or 'insert' -- or - use the Database.xxx methods. In the latter case, you can do partial batch updates.
Your code fragment to detect and process Database.saveResult[] is correct
Thank you. s
* You can't/shouldn't use Database.xxx methods on the triggered SObject X
I interpret statement that the triggered sObject in this case the Account, I can't/shouldn't use database methods because before triggers happen on the triggered object so due the order of execution, I don't/can't use dml/database methods execute the insert/update
Any other custom client that invokes your triggers has to be designed to handle partial updates -- if written in APEX, by using the Database.xxx methods
I interpret this statement that when the trigger is fired from a custom app, database methods need to be used but this is where i get confused. isn't that contradictory to the above? my apologies if i am not understanding that
Your last statement validates that the code stub to detect and process Database.saveResult[] is correct but how can I used the .saveResult[] method if I can't use the Database.insert statements in a before trigger?
wanted to understand before i start using database methods to handle partial success in my before trigger
sfdcls:
Let me try this another way:
Use Case 1 - User clicks New or Edit on an SFDC SObject X. No VF controller is used
1. The before Trigger is invoked
2. Trigger.new will contain a list of size 1 - the SObject x that was edited/created
3. Trigger.new will contain all the values from SObject x including the edited/typed-in values from the form
4. The Trigger code contains logic to do validations (that can't be done with SFDC Validation Rules) and derivations of other fields in x that you want the code to do, rather than make the user enter the values
5. The Trigger script ends..reaches the last statement. Assume no calls to addError were made
6. SFDC fires Validation Rules
7. Assume no Validation rules failed
8. Assume no workflows or after triggers
9. SFDC commits x
Use Case 2 - User does mass edit from inline View
1. The before Trigger is invoked
2. Trigger.new will contain a list of size n -all SObject x that were mass edited
3. Trigger.new will contain all the values from a list of SObject x including the new values
4. The Trigger code contains logic to do validations (that can't be done with SFDC Validation Rules) and derivations of other fields in list of x that you want the code to do, rather than make the user enter the values
5. The Trigger script ends..reaches the last statement. Assume no calls to addError were made
6. SFDC fires Validation Rules
7. Assume no Validation rules failed
8. Assume no workflows or after triggers
9. SFDC commits all list of x
Use Case 3 is Data Loader - it works just like Use Case 2
Use Case 4 is an after trigger on Y that updates 5 X's
0. After Trigger on Y uses Database.xxx methods on X
1. The before Trigger on X is invoked
2. Trigger.new will contain a list of size 5 -all SObject x that were part of step 0
3. Trigger.new will contain all the values from a list of SObject x including the new values
4. The Trigger code contains logic to do validations (that can't be done with SFDC Validation Rules) and derivations of other fields in list of x that you want the code to do, rather than make the user enter the values
5. The Trigger script ends..reaches the last statement. Assume (for example) 3 of the 5 x's had calls to addError
6. SFDC fires Validation Rules only on the two error-free x
7. Assume these Validation rules succeed
8. Assume no workflows or after triggers
9. SFDC commits 2 of the 5 x's
10. after trigger regains control and uses Database.saveResult[] to examine the success of step 0. Three of the SaveResult[] members will have isSuccess() = false
Based on these use cases, you should be able to see that the before trigger on X shouldn't do DML operations on X because SFDC does that implicitly when the trigger script hits the end (well, after a few more things have to pass - see SFDC Order of Execution).
But, code that deliberately makes a database operation on X can be written to do partial updates by using the Database.xxx methods and it is up to the code that makes those DML operations on X to decide what it should do if there are partial successes. This is application specific. For something like Data Loader, this is an easy decision for the Data Loader designers to do -- if in a batch of 50, 3 fail, the other 47 will commit as Data Loader treats each row update/insert as independent.
That is the beauty of writing all SFDC triggers bulkified -- they enable the callers to do batch operations. When the caller is a form, only one record is in the Trigger.new list -- but this is just the degenerate case.
This is probably one of the best threads I have read so far. Thank you for the detail and illustrations for each use case. Due to this explanation, I was able to perform a bulk update with the dataloader and resulted in partial successes. Notice the for loop starts at the top of the try block that uses the same sObject var so that .addError can be isolated to a single record instead of rejecting the entire batch. I plan on fine tuning the error handlers now to make the validation look cleaner. Let me know if you see any other issues but I think it looks good now - BIG THANKS ERIC.
So..you mention in a prior thread, dataloader will implicitly handle partial successes otherwise we would have explicitly reference it in our code.
Question I have is from a batch negative / partial test coverage perspective if I want to test negative and partial success in the same test method, we need to expliclty use database.insert instead of a DML operation since it would roll back the entire transaction which wouldn;t cover the partiai success test, correct? Last question is, I haven;t implemented batch apex before but if this was millions of records for Accounts, could I use the same utility class in conjunction with a batch apex class that handles the transaction async? How would that work?
List<Zone__c> zList = [select id, recordTypeId from Zone__c];
Map<Id,RecordType> rtMap = new Map<Id,RecordType>([Select id,Name from RecordType where sObjectType IN
('Account','Zone__c')]);
for(Account a:aList)
{
try
{
for(integer i= 0; i< zList.size(); i++)
{
if( rtMap.get(a.recordtypeid).Name ==rtMap.get(zList.get(i).recordtypeid).Name)
{
a.Zone__c = zList.get(i).id;
a.OwnerId = zList.get(i).rep__c;
}
}
}
catch (System.NullPointerException e)
{
a.addError('There was an error with the record since a field was not populated correctly, please contact IT at
1888-888-8888 for asssitance, error number ' + e.getLineNumber());
}
catch (System.queryException e)
{
a.addError('There was an error with the record since the system recognize zero or multiple Zone records, please
contact IT at 1888-888-8888 for assistance, error number ' + e.getLineNumber());
}
}
}
}
sfdcls:
Glad I was able to help.
1. How to test for partial success?
The technique I use is a bit hacky but you could use the Test.isRunningTest() method directly in your production code.Then, you could create a test method that set some static variable Z to true and then, in the production code if Test.isRunningTest() && Z, force a database error (query exception, null pointer exception, validation rule failure) on, say, every 10th record in the batch. Then your test method can detect whether the records 0-9, 11-19, etc passed and records 10,20, .. failed.
2. Batch APEX is just another example of a client that wants to update many X's - code it with Database.updates (inserts) upon X and allow for partial successes with the optAllOrNone argument. Your trigger will execute just like it does in the DataLoader Use Case above - each record that has an error will issue an addError and the batch APEX code can decide what to do with partial successes -- such as send an email with a log of # successes/#failures
If you haven't already, please mark one of the above posts as a Solution
Thank you crop-
i will see if I can pass coverage today. Couple question on batch apex
1. I read that for batch apex, interface database.batchable needs to be written. I also understand how this is similar to the dataloader use case. However, how does the trigger invoke this class.? I am not sure where the batch apex class falls between the trigger and apex class that has my business logic. Do I simply just add this batch apex class.method name in the trigger?
2. As programmers, we should always develop scalable code. What should be considered when apex needs to be written in batch apex? I ask because shouldn't we assume that the data will out-grow the standard governor limits(e.g. 10k transaction in single dml). If this is the case, shouldn't we always code using batch apex? Or perhaps the question is what should be async vs sync.
3. Assuming that #1 was true(i need 2 classes, i trigger), what happens to the governor limits in the apex class that has my business logic.
sfdcls:
You'll need two classes (plus your usual trigger code):
1. A class that creates an instance of the batchable class 'MyBatchableClass' and then calls Database.executeBatch(..) - this class gets invoked by some user action or external event.
2. A batchable class MyBatchableClass (read the APEX Developers Guide here) that implements four methods -- a constructor(..), start(..), execute(..), and finish(..).
The trick is:
1> you provide through the constructor the SOQL query to select your rows (all one million of them)
2> On the start method (called by SFDC), you use the Database.getQueryLocator() on the SOQL string to get a cursor to the result set
3> On the execute() method (also called by SFDC) , you'll be passed from SFDC 200 (or some other batch size, configurable) set of rows of your SObject X. This execute method then does a Database.update on all these rows (the 200). This will call your trigger. You do any error processing on Database.SaveResult[]
SFDC repeatedly calls the execute() method until the full result set from the SOQL query is processed. This could take many minutes depending on size of the set
4> Finally, the finish() method is called by SFDC. This is where you write an email message or other log type action with the results of the batch
The discussion about whether you should always have batch APEX coded really depends on your use cases. Do you need to mass update thousands of rows in a single user event or external system event? This is a whole other topic and is well-addressed better than I ever could in this book by Dan Appleman 'Advanced Apex Programming for Salesforce.com and Force.com'.
I'll let others weigh in here but most triggers are unlikely to exceed governor limits unless the trigger's caller passes in a long list of objects or the work to process each object requires many script statements. Again, Mr. Appleman's book takes you through all this in detail
found another twister here......since this is a before trigger. dmlExceptions can not be caught so this is a situation where user updates an existing record through the UI and transaction comes to a hault with .addError. Therefore, if I don't handle the dml exception and Run Test, I will hit 100% but will cause a test method failue. If I don't want the test failure then I have to assert it but would only be able to hit 86%. Which one is better??
catch (System.NullPointerException e)
{
// process exception with .addError
}
catch (System.DmlException e)
{
// process exception
}
Test Class
catch(System.NullPointerException e)
{
System.assertEquals('System.NullPointerException' , e.getTypeName());
}
catch(System.DmlException e)
{
System.assertEquals('System.DmlException', e.getTypeName() );
}
}
Ok... nevermind. I just found out that you can't execute DML operations on RecordType objects. Looks like the only way is to hard-code the RecordType.DeveloperName or RecordType.Name. Also, is there a benefit to using developername versus name?
I use DeveloperName only because it is less likely to be inadvertently changed in the Force.com UI
So I had to add a second method to my accou utility class and upon account insert or update, it updates the related contacts which is why I can use the before trigger and it works great but noticed at the end, i am processing the records with database.update and checking through the list for errors. However, if i want to display the error message on the account associated to these contacts, how would I throw a custom message on the screen without .addError?
public static void test(List<Account> aNewList, List<Account> aOldList )
{
for(Account a: aNewList)
{
//
for(integer i=0; i< aNewList.size(); i++)
{
// opt out check
{
acctsMap.put(a.Id, a);
}
}
//for loop key set
{
//assign to variables
}
/Database.update(updatedContacts);
Database.SaveResult[] lsr = Database.update(updatedContacts, false);
for(Database.SaveResult sr : lsr)
if (!sr.isSuccess())
{
// need a custom message but how do i display it here????
}
}
Assuming you are not using VF and are instead using the standard pages, then the best you can do is create a custom rich text field 'Account.Errors__c' and then populate this with your own error message, then update the Account (via update/Database.update if using an after trigger)
In this rich text field, you can use APEX to create HTML tags to make the message red/orange/green.
Thus, the transaction is committed but any 'informational messages' can be preserved on the record for inspection. Of course, you'll need to clear this Account.Errors__c field out on the next Account update ...or do some sort of running log
Forgive me if I've gotten a bit lost on this whole thread as it has been going on for a while and I've been diverted onto my own work <g>
crop1645 - I appreciate your time. No problem, we all have our own jobs and I was fortunate enough to get some great advice. The rich text field is very creative and I'm see if I can do that just for fun. I think I may just use a standard DML update on contacts and handle the exception with.adderror...thanks again
Hi crop i deployed my trigger but the method that processes the opt out is hitting a governor limit of > 10000 contacts? How come this is happening when i don;t have 10k contacts per account
sfdcls --
1. Given how much you have done since the beginning of this interaction, please repost your trigger and any supporting classes -- PLEASE use the code insert button in the post so it is easier to read.
2. Turn your production debug log on and repeat your transaction for the submitting user. then look at the log to see why you may be hitting a limit. You may be fetching all Contacts rather than just 'contacts ...where accountID IN {..}'