Hi!

In my project for GAE I am trying to use JDO with detached objects and
optimistic locking. I make the modifications to the detached instance
and then I update the database by calling
PersistenceManager.makePersistent(detachedObject). Then I read some
values from the persistent instance to be displayed in the view.
Finally, before I close the PersistenceManager I detach the persistent
instance and store it in the http session, so that it can be used in
the following request. All this used in combination with optimistic
locking should detect any concurrent modification to the database.

All of this works fine except when I try to access the keyId field of
the reattached persistent instance that has encoded string id, or the
ancestor field of a child. In these cases any concurrent modification
to the same entity will go undetected and any mofiications will be
silently overwritten. This only happens if I access these special kind
of fields, accessing normal properties will not cause such behavior.

Some examples:

The domain object with encoded string id:

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.Version;
import javax.jdo.annotations.VersionStrategy;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable(detachable="true",
identityType=IdentityType.APPLICATION)
@Version(strategy = VersionStrategy.VERSION_NUMBER, column =
"version")
public class DomainObjectWithEncodedStingId {

        @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
        @Extension(vendorName="datanucleus", key="gae.encoded-pk",
value="true")
        private String id;

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-id",
value="true")
    private Long keyId;

        String value;

        public String getId() {
                return id;
        }

        public void setId(String id) {
                this.id = id;
        }

        public Long getKeyId() {
                return keyId;
        }

        public void setKeyId(Long keyId) {
                this.keyId = keyId;
        }

        public String getValue() {
                return value;
        }

        public void setValue(String value) {
                this.value = value;
        }

}

The corresponding failing test:


        public void testVersioningUpdateDetachedEncodedStringId() {
                PersistenceManager pm;
                Transaction tx;

                // The first user reads the data in the first request
                // and stores it in http session
                pm = pmf.getPersistenceManager();
                tx = pm.currentTransaction();
                tx.begin();

                DomainObjectWithEncodedStingId object = new
DomainObjectWithEncodedStingId();
                object.setValue("value1");
                pm.makePersistent(object);

                // Keep the detached instance for the next request
                DomainObjectWithEncodedStingId detachedObject = pm.detachCopy
(object);

                tx.commit();
                pm.close();

                // Concurrently modify the same instance,
                // simulating another user updating the same data
                pm = pmf.getPersistenceManager();
                tx = pm.currentTransaction();
                tx.begin();

                DomainObjectWithEncodedStingId loaded1 = pm.getObjectById
(DomainObjectWithEncodedStingId.class, detachedObject.getId());
                loaded1.setValue("dataSetByUser2");

                tx.commit();
                pm.close();


                // The original user also updates the data
                pm = pmf.getPersistenceManager();
                tx = pm.currentTransaction();
                tx.begin();

                detachedObject.setValue("dataSetByUser1");
                DomainObjectWithEncodedStingId attached = pm.makePersistent
(detachedObject);

                // If I access the keyId field of the attached instance
                // then the concurrent modification will not be detected.
                // If I remove this line, or access only data properties,
                // like attached.getValue(), then the test works as expected;
                attached.getKeyId();

                try {
                        detachedObject = pm.detachCopy(attached);

                        // We should not get to this point as the
                        // concurrent modification should be detected
                        // when the instance is detached.
                        Assert.fail("Should not succeed!");
                } catch (JDOOptimisticVerificationException e) {
                        e.printStackTrace();
                }

                tx.commit();
                pm.close();

        }

The concurrent modification is similarly undetected if I try to access
the ancestor field of a reatteched instance that has been declared
like this:

        @Persistent
    @Extension(vendorName="datanucleus", key="gae.parent-pk",
value="true")
    Key parentKey;

There is a workaround by first detaching the instance to be stored for
later request and only accessing the fields of the persistent instance
after that. This way the concurrent modification is detected when the
instance is detached.

Still I do not think this is expected behavior and it might be raised
as an issue.


I am using Appengine SDK 1.2.2.

Marton

--~--~---------~--~----~------------~-------~--~----~
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 [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to