You need to sign in to do that
Don't have an account?
doughauck-corp
Visualforce DML changes do not persist
I am having a problem with the upsert DML command in an Apex class I'm using with my Visualforce page.
The user makes changes on a generic SObject, which is exposed as the editSObject property of my custom Apex class, called SObjectTreeNode (I'm not very inventive with names, sorry). When the user hits the Save button, the SObjectTreeNode.upsertSObject() method is called to send the changes to the database, and then the page is refreshed.
When the page refreshes right after a Save, the new values show up in the changed fields just as they should. Since refreshing the page includes a re-query of the associated SObject, I assumed this meant my changes were in the database. Not so, though - refreshing the screen a second time, or navigating to the normal SFDC detail page for the object, shows the fields back at their original values.
Below is the upsertSObject() method - with some extra debug code - and its associated output. (Please note that User__c is a custom text-only field that has no connection to the SFDC User object. Sorry for the confusion, but I didn't name it. I picked it for my example because I know there are no WF or Validation rules on this field, but all fields show the same behavior.)
Here is the code:
And here is the log output:
As you can see, it's the same record before and after the upsert, but the value returned by the SOQL statement has changed to match the upserted value. Any further queries in this same transaction - up until the page has been fully refreshed and my log comes to an end - will show the new value. Any further refreshes, or navigation through normal SF pages, and the fields go back to their original values.
As far as I can tell, there are no errors encountered in this transaction, hence no reason to roll back changes. (And my understanding is that rolled back changes wouldn't show up in a subsequent SOQL query, anyway.) I am working with Visualforce, and in a Sandbox, if that makes any difference. I can't find any documentation that says it should matter, though.
Can anyone tell me, what gives?
Thanks,
Doug
The user makes changes on a generic SObject, which is exposed as the editSObject property of my custom Apex class, called SObjectTreeNode (I'm not very inventive with names, sorry). When the user hits the Save button, the SObjectTreeNode.upsertSObject() method is called to send the changes to the database, and then the page is refreshed.
When the page refreshes right after a Save, the new values show up in the changed fields just as they should. Since refreshing the page includes a re-query of the associated SObject, I assumed this meant my changes were in the database. Not so, though - refreshing the screen a second time, or navigating to the normal SFDC detail page for the object, shows the fields back at their original values.
Below is the upsertSObject() method - with some extra debug code - and its associated output. (Please note that User__c is a custom text-only field that has no connection to the SFDC User object. Sorry for the confusion, but I didn't name it. I picked it for my example because I know there are no WF or Validation rules on this field, but all fields show the same behavior.)
Here is the code:
public SObjectTreeNode upsertSObject() { if(!isEditable) throw new sObjectTreeNodeException('Error in node \'' + nodeName + '\': upsertSObject() can only be called on Editable nodes.'); if(editSObject == null) throw new sObjectTreeNodeException('Error in node \'' + nodeName + '\': editSObject is null.'); system.debug(' '); system.debug('Running upsertSObject()...'); id editSObjId = (id)editSObject.get('id'); sObject dbaseSObject = [select Id, User__c from Inspection__c where Id = :editSObjId limit 1]; system.debug(' '); system.debug('Pre-Upsert Values:'); system.debug(' - editSObject.id = ' + editSObject.get('id')); system.debug(' - editSObject.User__c = ' + editSObject.get('user__c')); system.debug(' - dbaseSObject.id = ' + dbaseSObject.get('id')); system.debug(' - dbaseSObject.User__c = ' + dbaseSObject.get('user__c')); upsert editSObject; editSObjId = (id)editSObject.get('id'); dbaseSObject = [select Id, User__c from Inspection__c where Id = :editSObjId limit 1]; system.debug(' '); system.debug('Post-Upsert Values:'); system.debug(' - editSObject.id = ' + editSObject.get('id')); system.debug(' - editSObject.User__c = ' + editSObject.get('user__c')); system.debug(' - dbaseSObject.id = ' + dbaseSObject.get('id')); system.debug(' - dbaseSObject.User__c = ' + dbaseSObject.get('user__c')); system.debug('Completed upsertSObject()'); system.debug(' '); return this; }
And here is the log output:
20:06:06.3 (2285501897)|USER_DEBUG|[85]|DEBUG|Running upsertSObject()... 20:06:06.3 (2298707241)|USER_DEBUG|[90]|DEBUG| 20:06:06.3 (2298739034)|USER_DEBUG|[91]|DEBUG|Pre-Upsert Values: 20:06:06.3 (2298843638)|USER_DEBUG|[92]|DEBUG| - editSObject.id = a1HS0000001hrsRMAQ 20:06:06.3 (2298949438)|USER_DEBUG|[93]|DEBUG| - editSObject.User__c = Barney Rubble 20:06:06.3 (2299062140)|USER_DEBUG|[94]|DEBUG| - dbaseSObject.id = a1HS0000001hrsRMAQ 20:06:06.3 (2299176216)|USER_DEBUG|[95]|DEBUG| - dbaseSObject.User__c = Fred Flintstone 20:06:06.3 (2383815970)|USER_DEBUG|[102]|DEBUG| 20:06:06.3 (2383854572)|USER_DEBUG|[103]|DEBUG|Post-Upsert Values: 20:06:06.3 (2384028718)|USER_DEBUG|[104]|DEBUG| - editSObject.id = a1HS0000001hrsRMAQ 20:06:06.3 (2384148671)|USER_DEBUG|[105]|DEBUG| - editSObject.User__c = Barney Rubble 20:06:06.3 (2384263266)|USER_DEBUG|[106]|DEBUG| - dbaseSObject.id = a1HS0000001hrsRMAQ 20:06:06.3 (2384363465)|USER_DEBUG|[107]|DEBUG| - dbaseSObject.User__c = Barney Rubble 20:06:06.3 (2384393412)|USER_DEBUG|[109]|DEBUG|Completed upsertSObject()
As you can see, it's the same record before and after the upsert, but the value returned by the SOQL statement has changed to match the upserted value. Any further queries in this same transaction - up until the page has been fully refreshed and my log comes to an end - will show the new value. Any further refreshes, or navigation through normal SF pages, and the fields go back to their original values.
As far as I can tell, there are no errors encountered in this transaction, hence no reason to roll back changes. (And my understanding is that rolled back changes wouldn't show up in a subsequent SOQL query, anyway.) I am working with Visualforce, and in a Sandbox, if that makes any difference. I can't find any documentation that says it should matter, though.
Can anyone tell me, what gives?
Thanks,
Doug
Could you check if there are any triggers or workflow associated with this object.
Also do share the vf and controller constructor code to help you better.
Is SObjectTreeNode gets called during button click? and is there any significance in your code for returning 'this' in this place? , i mean is this returning the current class instance reference?
Thanks and Regards,
Shiva RV
Thanks for the response. Additional code is below; in the meantime, here is some further explanation, as well as answers to your specific questions:
- First, an overview of the full application (because you never know what might jump out at you):
- As you may have guessed from the name, SObjectTreeNode is a tree node that contains an SObject. My intent is to use it for working with records that have a hierarchical structure. (e.g. Account->Contact->Cases). The class has has an associated VF component,
- Each node has a nodeSObject property, which stores the associated SObject while making/manipulating the tree. However, because this class is primarily to be used with VF, and I don't want 500 SObjects serialized into the VF view state, this property is marked transient.
- Since the only time there is a need to store the SObject on the client side is if the user needs to Edit it, there is a separate, non-transient property, editSObject, which points to the same SObject, but only on nodes marked editable (isEditable=true); otherwise it is null, so it doesn't get serialized. In my code above, note that it is editSObject that is used in the upsertSObject() method.
- There is also a displayList property, a List<string> that holds the SObject field names I want displayed for that node.
- Finally, there is a VF component for displaying the node in either View or Edit mode; the component uses a <repeat> block to loop through the values in displayList and assign the relevant fields to either <outputField> or <inputField> tags.
- Now to your actual questions:
- There are WF rules associated with certain fields on the Inspection__c object, though not with the User__c field. There are no active triggers, flows, or processes associated with this object.
- The method that calls upsertSObject() - and does just about everything else - is the inspectionNode property getter on the main page controller. The getter calls the static createInspectionTree() method of the main controller to build the tree and return the top node (which contains the whole tree). It re-creates the tree every time the getter is called, because of the transient SObjects.
- There are two other properties which are used here: nodesToEdit is a list of nodes that need to be put in Edit mode upon refresh, and nodesToSave is a list of nodes that need to be saved before refresh. If the user clicked the Edit button on a node, a reference to it is put into nodesToEdit, and if Save button is clicked on a node, a reference is put into nodesToSave.
- The inspectionNode getter and the createInspectionTree() method use these two lists to determine how the tree should be rebuilt, and what should be done before returning it to the VF page. The getter calls upsertSObject() on every node in the nodesToSave list before it returns the updated tree, and that is what you are looking at in the code / results above.
Below is my code, or at least that part of it that works with creating and displaying the tree. I know it's messy and needs a lot of work after this - it's very much in the initial stages. However, I don't think there's anywhere that the mess should affect the behavior of the Upsert DML - I don't see anywhere that the Upsert could be double-called, or the SObject overwritten after the part you see in my results above.There's a lot of code below, but when (if) I get an answer, I will edit the post to remove unnecessary stuff.
Thanks,
Doug
Relevant parts of the SObjectTreeNode class:
The node display VF component:
The VF component's controller:
The main VF page (in its current form, it will only display the top node; I have everything else commented out to avoid distractions):
And finally, here are the relevant parts of the main VF page controller: