That was what was driving me mad.

I was able to persistently navigate to it in the collection of the parent
object (in the initial new state).
Also DN logged it making the object persistent.
  Nov 29, 2009 9:45:58 PM
org.datanucleus.ObjectManagerImp persistObjectInternal
  FINE: Making object persistent
: "com.ninuku.diary2.server.diary.texten...@13c7edf"

Thanks again for unblocking me on this issue. This JDO ORM layer is a really
tough nut to crack. I used to evangelize pure ODBMS for ObjectDesign and
thought that was hard. Your JDO snipets that work series is a real boon.
Please consider formalizing them.


On Tue, Dec 1, 2009 at 9:12 PM, Max Ross (Google)
<maxr+appeng...@google.com<maxr%2bappeng...@google.com>
> wrote:

> :-)  Happy to help Andy.
>
> I'm not sure what is triggering the commit of the new object since I don't
> see you calling pm.makePersistent() anywhere.  How are you concluding that
> the TextEntry is actually persisted?
>
> Max
>
> On Tue, Dec 1, 2009 at 12:25 PM, a stevko <andy.ste...@gmail.com> wrote:
>
>> 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<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.


Reply via email to