Search code examples
google-app-enginejdorequestfactory

JDO + RequestFactory - entity versioning


I was trying to set-up a really trivial RequestFactory example, but I failed. I persist entities in to my datastore just find, however when trying to pull them out again, i get a

com.google.web.bindery.requestfactory.server.UnexpectedException: The persisted entity with id aglub19hcHBfaWRyCgsSBFVzZXIYBAw has a null version

So first of all this is my JPO annotated entity class. At the end you find to static function for RequestFactory to call, and a non-static member function which will become an InstanceRequest.

package com.test.server;

import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.annotations.Column;
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;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
@Version(strategy = VersionStrategy.VERSION_NUMBER, column = "VERSION", extensions = {         @Extension(vendorName = "datanucleus", key = "field-name", value = "version") })
public class User {

public User() {

}

public User(String name) {
    this.name = name;
}

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

@Persistent
@Column(name = "version")
private Integer version;      

@Persistent
private String name;

public String getId() {
    return id;
}

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

public Integer getVersion() {
    return version;
}

public void setVersion(Integer version) {
    this.version = version;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

  public static final PersistenceManager persistenceManager() {
        return PMF.get().getPersistenceManager();
      }

  @SuppressWarnings("unchecked")
  public static List<User> findAllUsers() {
      PersistenceManager pm = persistenceManager();
      try {
          String query = "SELECT FROM " + User.class.getName();
          List<User> objects = (List<User>) pm.newQuery(query).execute();
          objects.size(); // This is the workaround to retrieve all objects
          return objects;
      } finally {
          pm.close();
      }
  }

  public static User findUser(String id) {
      PersistenceManager pm = persistenceManager();
      try {
            User u = pm.getObjectById(User.class, id);
            return u;
      } finally {
          pm.close();
      }
  }

  public void persist() {
        PersistenceManager pm = persistenceManager();
        try {
          pm.makePersistent(this);
        } finally {
          pm.close();
        }
      }
  }

The RequestFactory interface itself is really simple

 package com.test.shared;

 import com.google.web.bindery.requestfactory.shared.RequestFactory;

 public interface UserOrderRequestFactory extends RequestFactory {
UserRequest userRequest();
 }

so is the corresponding RequestContext

package com.test.shared;

import java.util.List;

import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.google.web.bindery.requestfactory.shared.Service;
import com.google.web.bindery.requestfactory.shared.Request;
import com.test.server.User;

@Service(User.class)
public interface UserRequest extends RequestContext {
    Request<List<UserProxy>> findAllUsers();
    InstanceRequest<UserProxy, Void> persist();
}

Here is the proxy of user for the client side

package com.test.shared;

import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.ProxyFor;

@ProxyFor(com.test.server.User.class)
public interface UserProxy extends EntityProxy {
    EntityProxyId<UserProxy> stableId();
    String getName();
    void setName(String name);
}

and finally my onModuleLoad() which first persists a user and then gets a list of all users.

public void onModuleLoad() {

    final EventBus eventBus = new SimpleEventBus();
    requestFactory = GWT.create(UserOrderRequestFactory.class);
    requestFactory.initialize(eventBus);

    UserRequest userRequest = requestFactory.userRequest();

    UserProxy user = userRequest.create(UserProxy.class);
    user.setName("Luigi");

    userRequest.persist().using(user).fire( new Receiver<Void>()
            {
              @Override
                public void onSuccess(Void arg0)
                {
                    GWT.log("User persisted.");
                }
            });


    userRequest = requestFactory.userRequest();
    Request<List<UserProxy>> findAllUsersRequest = userRequest.findAllUsers();
    findAllUsersRequest.fire( new Receiver<List<UserProxy>>() {
        @Override
        public void onSuccess(List<UserProxy> list) {
            for(UserProxy u: list) {
                GWT.log(u.getName());
            }
        }
    });

Any input is welcome. I would be happy to receive any advice on this.

Thank you in advance.


Solution

  • While JPA seems to do this automatically it seems to be my job to advance the version counter in JDO. I added the following code to my persist routine in User.java

    // JPA @Version does this automatically, but JDO @Version is not working like that. Not sure why.
    if (version == null) {
     version = 0l;
    }
    version++;