WOW Max! You deserve a gold star for your insight, TY! TY! TY! I added a PM parameter to my lookup utility layer and the code suddenly works as desired. While I was using a static PMFactory, I didn't realize that calling get() would begin a new transaction context every time, It does makes sense given the platforms 1 txn per entity group constraint.
What I don't understand is what triggered the commit of the new object. I would assume the commit would be triggered by the 2nd pm going out of scope but it was already out of scope when the parent object was passed back from the lookup method. --Andy On Tue, Dec 1, 2009 at 10:23 AM, Max Ross (Google) < maxr+appeng...@google.com <maxr%2bappeng...@google.com>> wrote: > My suspicion is that in the update case you are starting your txn with a > PersistenceManager, then using a different PersistenceManager to load the > TextEntry (diaryManager.getTextEntryById(). I can't be sure because you > haven't posted the code for diaryManager, but if it does the same thing as > lookupTextChapterById() and pmp.get() creates a new PersistenceManager, then > this is probably what is going on. You're not seeing the updates when you > commit the txn because the txn is not associated with the PersistenceManager > that loaded the TextEntry. I'd recommend using the same PersistenceManager > for all these operations. > > Hope this helps, > Max > > > On Sun, Nov 29, 2009 at 4:00 PM, Stevko <andy.ste...@gmail.com> wrote: > >> I'm struggling with a JDO data loss situation. >> The code below will NEW a TextEntry object and hook it into the >> object tree just fine. >> The copy DTO updates are performed without error/exception thrown but >> all updates are lost. >> The fields that are being lost are privateComment, byLine, >> shortContent, longContent, longContentSize. >> >> I've attached a DN transaction log at the bottom. >> The object state is marked as 'dirty' but the ObjectManager does not >> 'put' the new string values. >> According to the logs, for some unknown reason, the DB >> DatastorePersistenceHandler commits the "new" object prior to the >> copy of the associated data from the action DTO into the persistent >> object. >> >> I've broken this up into two sequential transactions complete with >> isolated lookups but the data loss continues. >> >> //------------------------------------- >> // abridged code - error checking removed... >> // >> private final Provider<PersistenceManager> pmp; >> private PersistenceManager pm; >> >> @Override >> public SaveTextEntryResult execute(SaveTextEntry action, >> ExecutionContext context) throws ActionException { >> >> String entryId = action.getEntryId(); >> SaveTextEntryResult result; >> try { >> pm = pmp.get(); >> pm.currentTransaction().begin(); >> >> TextEntry entry; >> >> // ---------- >> // is this new or update? >> if (action.isNewEntry()) { >> >> // lookup the chapterid and verify accountid >> TextChapter chapter = >> diaryManager.lookupTextChapterById(action >> .getChapterId()); >> entry = new TextEntry(action.getTimestamp(), >> chapter, >> action.getSourceType(), >> action.getSourceId()); >> >> } else { >> // look up the textentry id >> entry = >> diaryManager.lookupTextEntryById(action.getEntryId()); >> } >> //---->>> new object is flushed here <<<------ >> // copy fields from action to object >> entry.setPrivateComment(action.isPrivateComment()); >> entry.setByLine(action.getByLine()); >> logger.info("SaveTextEntry() short content =" + >> action.getShortContent() ); >> entry.setShortContent(action.getShortContent()); >> logger.info("SaveTextEntry() long content =" + >> action.getLongContent >> () ); >> entry.setLongContent(action.getLongContent()); >> >> //---->>> persist object updates are not flushed here <<<------ >> pm.currentTransaction().commit(); >> >> result = new SaveTextEntryResult(true, entry.getId() ); >> >> } finally { >> >> if (pm.currentTransaction().isActive()) { >> logger.severe("SaveTextEntry caught exception"); >> result = new SaveTextEntryResult(false, ""); >> pm.currentTransaction().rollback(); >> } >> } >> >> return result; >> } >> //------------ >> // related method below >> public TextChapter lookupTextChapterById(String id) { >> pm = pmp.get(); >> >> TextChapter chapter = pm.getObjectById(TextChapter.class, >> id); >> >> return (chapter); >> } >> >> //---------------------------------------------------------------- >> // Persistent object definition (bean methods removed) >> >> @PersistenceCapable(identityType = IdentityType.APPLICATION ) >> @FetchGroup( name="children", members={ >> @Persistent(name="commentEntrys"), >> @Persistent(name="chapter") >> }) >> public class TextEntry implements Entry, Serializable, >> Comparable<TextEntry> { >> >> @PrimaryKey >> @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) >> @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = >> "true") >> private String id; >> >> @Persistent >> private Long accountId = new Long(0); >> >> // Parent collection with ArrayList<TextEntry> >> @Persistent >> private TextChapter chapter; >> >> @Persistent >> private Long timestamp = new Long(0); >> >> @Persistent >> private boolean privateComment = false; >> >> @Persistent >> private String byLine = ""; >> >> @Persistent(defaultFetchGroup="true") >> private SourceType source = SourceType.UNKOWN; >> >> @Persistent >> private String sourceId = ""; >> >> @Persistent(defaultFetchGroup="true") >> private String shortContent = null; >> >> @Persistent(defaultFetchGroup="true") >> private String longContentString = null; >> >> @Persistent(defaultFetchGroup="true") >> private Long longContentSize = new Long(-1); >> >> @Persistent(mappedBy = "textEntry") >> @Order(column="TEXTENTRY_COMMENTENTRY_IDX") >> private TreeSet< CommentEntry> commentEntrys = new TreeSet< >> CommentEntry>(); >> >> public TextEntry(Long timestamp, TextChapter chapter, SourceType >> source, >> String sourceId) { >> this.timestamp = timestamp; >> this.source = source; >> this.sourceId = sourceId; >> >> // setup external references >> this.chapter = chapter; >> this.accountId = chapter.getAccountId(); >> chapter.add(this); >> } >> >> >> //------------------------------------------------------------------------------------------------------- >> // this is a log showing the point where the >> DatastorePersistenceHandler commits the fields. >> >> Nov 29, 2009 9:45:58 PM org.datanucleus.ObjectManagerImpl >> persistObjectInternal >> FINE: Making object persistent : >> "com.ninuku.diary2.server.diary.texten...@13c7edf" >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: Putting entity of kind TextEntry with key Diary(4)/TextChapter >> (7)/TextEntry(no-id-yet) >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: timestamp : 1259531142320 >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: TEXTCHAPTER_TEXTENTRY_IDX : 7 >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: accountId : 2 >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: shortContent : null >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: source : USER_MANUAL >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: longContentSize : -1 >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: privateComment : false >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: byLine : >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: longContentString : null >> Nov 29, 2009 9:45:58 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler put >> FINE: sourceId : 1259531142320 >> Nov 29, 2009 9:45:58 PM org.datanucleus.sco.backed.TreeSet <init> >> FINE: Object "com.ninuku.diary2.server.diary.texten...@13c7edf" field >> "commentEntrys" is replaced by a SCO wrapper of type >> "org.datanucleus.sco.backed.TreeSet" [cache-values=true, lazy- >> loading=true, queued-operations=false, allow-nulls=false] >> Nov 29, 2009 9:45:59 PM >> org.datanucleus.store.appengine.DatastorePersistenceHandler get >> FINE: Getting entity of kind Diary with key Diary(4) >> Nov 29, 2009 9:46:22 PM >> com.google.appengine.api.datastore.dev.LocalDatastoreService >> $PersistDatastore persist >> INFO: Time to persist datastore: 10 ms >> Nov 29, 2009 9:46:32 PM >> com.ninuku.diary2.server.handler.SaveTextEntryHandler execute >> INFO: SaveTextEntry() short content =this is a short content update >> Nov 29, 2009 9:46:37 PM >> com.ninuku.diary2.server.handler.SaveTextEntryHandler execute >> INFO: SaveTextEntry() long content =this is a long content update >> Nov 29, 2009 9:46:40 PM org.datanucleus.TransactionImpl >> internalPreCommit >> FINE: Transaction committing for ObjectManager >> org.datanucleus.objectmanageri...@beef49 >> Nov 29, 2009 9:46:40 PM org.datanucleus.ObjectManagerImpl >> flushInternal >> FINE: ObjectManager internalFlush() process started - 0 dirty objects >> Nov 29, 2009 9:46:40 PM org.datanucleus.ObjectManagerImpl >> flushInternal >> FINE: ObjectManager internalFlush() process finished >> Nov 29, 2009 9:46:40 PM org.datanucleus.transaction.Transaction commit >> FINE: Committing [DataNucleus Transaction, ID=Xid= >> //----------------------------------------------------------------- >> // this is the entry object debugger showing the dirty fields... >> this SaveTextEntryHandler (id=151) >> action SaveTextEntry (id=169) >> context AbstractDispatch$DefaultExecutionContext (id=171) >> entryId >> "agxuaW51a3UtYWxwaGFyKwsSBURpYXJ5GAQMCxILVGV4dENoYXB0ZXIYBwwLEglUZXh0RW50cnkYFQw" >> (id=172) >> entry TextEntry (id=160) >> accountId Long (id=174) >> byLine "" (id=177) >> chapter TextChapter (id=178) >> commentEntrys TreeSet (id=181) >> >> id >> >> "agxuaW51a3UtYWxwaGFyKwsSBURpYXJ5GAQMCxILVGV4dENoYXB0ZXIYBwwLEglUZXh0RW50cnkYFQw" >> (id=183) >> jdoFlags 1 >> jdoStateManager JDOStateManagerImpl (id=188) >> objectValGenerators null >> activity ActivityState (id=205) >> associatedValuesMap HashMap<K,V> (id=207) >> changingState false >> cmd ClassMetaData (id=211) >> currFM null >> currFMmonitor Object (id=213) >> dirty true >> dirtyFields (id=216) >> [0] false >> [1] false >> [2] false >> [3] false >> [4] false >> [5] true >> [6] true >> [7] true >> [8] true >> [9] false >> [10] false >> [11] false >> embeddedOwners null >> fieldsToBeUpdatedAfterObjectInsertion null >> flushedNew false >> insertionNotifyList null >> jdoDfgFlags 1 >> loadedFields (id=218) >> loadingFieldsInFetchPlan false >> myFP FetchPlan$FetchPlanForClass (id=219) >> myID StringIdentity (id=221) >> myInternalID null >> myLC PersistentNontransactionalDirty (id=224) >> myOM ObjectManagerImpl (id=227) >> myPC TextEntry (id=160) >> myVersion null >> operationalFlags 0 >> pcObjectType 0 >> postLoadPending false >> preDeleteLoadedFields null >> referencedPC null >> relationManager null >> restoreValues false >> savedFlags 1 >> savedImage TextEntry (id=231) >> savedLoadedFields (id=232) >> storingPC false >> transactionalVersion null >> updatingForPostInsert false >> longContentSize Long (id=192) >> longContentString "this is a long content update" (id=173) >> privateComment true >> shortContent "this is a short content update" (id=168) >> source SourceType (id=200) >> sourceId "1259536113144" (id=203) >> timestamp Long (id=204) >> >> -- >> >> You received this message because you are subscribed to the Google Groups >> "Google App Engine for Java" group. >> To post to this group, send email to >> google-appengine-j...@googlegroups.com. >> To unsubscribe from this group, send email to >> google-appengine-java+unsubscr...@googlegroups.com<google-appengine-java%2bunsubscr...@googlegroups.com> >> . >> For more options, visit this group at >> http://groups.google.com/group/google-appengine-java?hl=en. >> >> >> > -- > You received this message because you are subscribed to the Google Groups > "Google App Engine for Java" group. > To post to this group, send email to > google-appengine-j...@googlegroups.com. > To unsubscribe from this group, send email to > google-appengine-java+unsubscr...@googlegroups.com<google-appengine-java%2bunsubscr...@googlegroups.com> > . > For more options, visit this group at > http://groups.google.com/group/google-appengine-java?hl=en. > -- You received this message because you are subscribed to the Google Groups "Google App Engine for Java" group. To post to this group, send email to google-appengine-j...@googlegroups.com. To unsubscribe from this group, send email to google-appengine-java+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-appengine-java?hl=en.