9

Usually if apex code attempts to access an sObject field that wasn't retrieved via SOQL I get an exception like:

System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Opportunity.Namespace_CustomField_c

This if useful, as it tells me to go back to the SOQL query that retrieved the Opportunity and add the CustomField__c field to the select.

However, I've noticed that the exception isn't always thrown. Instead the field in question is just null.

Has anyone else encountered this and have any idea why it happens?

As an example of the issue:

global with sharing class CustomController {
    public Opportunity opp {get; set;}

    global void method1(Id oppId) {
        opp = [Select o.Id, o.Type, o.IsWon, Error_Message__c 
               From Opportunity o where id =:oppId limit 1 FOR UPDATE];

        // Setting this field appears prevents any further SObjectExceptions
        // NB: This was added after the question was originally asked to highlight the issue. 
        opp.Error_Message__c = '';

        System.debug('opp.CustomField__c: ' + opp.CustomField__c);

        method2(opp);
    }

    public void method2(Opportunity opp) {
        if(opp.CustomField__c != null) {
            System.debug('DoStuff');
        }
    }
}
  1. Create an instance of a custom controller class CustomController
  2. Invoke the public method method1 on the above class that takes an Opportunity Id
  3. The public method method1 runs a SOQL query to retrieve the Opportunity and a number of fields, but not CustomField__c. The retrieved Opportunity is assigned to a public property opp on the controller.
  4. The Opportunity property opp is passed as a parameter to another method in the custom controller class. (I'm aware that this isn't necessary as the method would otherwise be able to see the value via the public property)
  5. The method method2 that received the Opportunity checks if the custom field is not null before attempting to use the value to do some other updates.

If I create an instance of the custom controller in anonymous apex and invoke the method from point 2 no SObjectException is thrown and the CustomField__c value is always null.

If I create an instance of the custom controller in anonymous apex, run the same SOQL query and pass the resulting Opportunity directly to the method in point 4 I get the expected SObjectException.

  • I thought I was imagining it, yes I have had this exact same problem, if I can remember the context I will post some more details – Daniel Blackhall Oct 24 '12 at 0:55
  • I wonder if it has to do with the Opportunity being an instance variable in the first case, and somehow instance variables have initial values (at least in the Java world leepoint.net/notes-java/data/variables/45local-inst-class.html) Would be interesting to see what value the CustomField turns out to have in case 1 in a system.debug. Is it equal to null? – techtrekker Oct 24 '12 at 0:57
  • Having said that, I cannot seem to reproduce what you've described, I get the sObject not retrieved error for Case 1 too! – techtrekker Oct 24 '12 at 1:26
  • @techtrekker, it might be that I've oversimplified the example. The actual controller in question is a bit of a Behemoth that would benefit from some refactoring. I'll try changing it to see what happens if the Opportunity isn't stored in an instance variable. – Daniel Ballinger Oct 24 '12 at 1:35
  • 1
    Cool. The only caveat would be that since sObjects are passed by reference, hopefully there isn't a sneaky bit of assignment which is setting the value of CustomField1 somewhere in the foodchain. – techtrekker Oct 24 '12 at 1:41
8

I cannot seem to reproduce what you've described, I get the sObject not retrieved error for Case 1 too!

The only caveat would be that since sObjects are passed by reference, hopefully there isn't a sneaky bit of assignment which is setting the value of CustomField1 somewhere in the foodchain.

If there is an assignment of value then it wouldn't throw the exception for any subsequent access. (Even though it wasn't initially retrieved as part of the soql)


As a simplified example, this will throw the SObjectException (assuming you have at least one Opportunity):

Opportunity opp = [select Id, StageName From Opportunity limit 1];
System.debug(opp.Probability);

And this will not (opp.Probablity will be null):

Opportunity opp = [select Id, StageName From Opportunity limit 1];
opp.StageName = 'foo';
System.debug(opp.Probability);
  • 1
    Thanks for the idea about assignments being the root cause of the issue. I find it odd that any assignment to the sObject prevents further SObjectExceptions on otherwise untouched fields. – Daniel Ballinger Oct 24 '12 at 2:20
  • 1
    See the comments under this question. I observed the same kind of thing happening the other day. – Peter Knolle Oct 24 '12 at 9:54
4

You could consider the sObjectSelector pattern that we use which assures us that whenever we select records for a particular sObject type, the fields of interest to us are always included.

OpportunitySelector.cls extends sObjectSelector.cls and makes use of StringBuilder.cls

  • That's a great idea. Sort of like addFields in a StandardController. – techtrekker Oct 24 '12 at 11:25
  • 1
    Actually, I have been using a modified version of the selector class pattern to provide the field list for StandardController.addFields() which is quite neat – Stephen Willcock Oct 24 '12 at 14:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.