I have a problem with the JDO implementation of a Google App that I cannot understand. The documentation ( http://code.google.com/intl/sv-SE/appengine/docs/java/datastore/jdo/creatinggettinganddeletingdata.html ) says: "The call to makePersistent () is synchronous and will not be returned until the object is saved, and indexes will be updated. " but my experience is different.
I want to save a (makePersistent) object in a data store. When the save is complete, I want to receive it (query execution) immediately from the data store. I know that I don’t need to retrieve it (because I already have an object in memory), but the fact is that I want the next request to be able to get data from the data store. This does not work with the current implementation if the second request is fast enough.
One strange thing I noticed is that if I try to retrieve an object from storage a couple of times in a loop (code below), the object returns very quickly (usually <10ms). But if I missed the loop and instead ran Thread.sleep (..) for 5000 ms between makePersistent and the request to execute it, I’m not sure that the object was found. None of these solutions is what I want. I want to be able to immediately receive data, not being between a dream or a cycle.
The code and result of accessing the DataStoreTestServlet is below, as you can see, including a loop that "waits" for the data to be found. Again, I don't want a loop.
Does anyone know what I am missing? I think it should be something. This implementation does not suit me :).
I am using appengine-java-sdk-1.6.0. This is a problem both locally (development server) and when deployed to Google servers.
Here is the result of accessing the servlet.
Created users: User [password=password, userName=user1321190966416] took 18ms, 2 loop(s) User [password=password, userName=user1321190966438] took 15ms, 6 loop(s) User [password=password, userName=user1321190966456] took 2ms, 1 loop(s) User [password=password, userName=user1321190966460] took 10ms, 5 loop(s) User [password=password, userName=user1321190966472] took 0ms, 1 loop(s) User [password=password, userName=user1321190966472] took 0ms, 1 loop(s) User [password=password, userName=user1321190966472] took 16ms, 1 loop(s) User [password=password, userName=user1321190966488] took 0ms, 2 loop(s) User [password=password, userName=user1321190966488] took 0ms, 1 loop(s) User [password=password, userName=user1321190966488] took 16ms, 1 loop(s)
Code and configuration.
jdoconfig.xml
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" /> </persistence-manager-factory> </jdoconfig>
PMF.java
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public final class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional"); private PMF() { } public static PersistenceManagerFactory get() { return pmfInstance; } }
User.java
import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class User { @PrimaryKey @Persistent private String userName; @Persistent private String password; public User(String userName, String password) { super(); this.setUserName(userName); this.setPassword(password); } public String getUserName() { return userName; } public String getPassword() { return password; } public void setUserName(String userName) { this.userName = userName; } public void setPassword(String password) { this.password = password; } public String toString() { return "User [password=" + password + ", userName=" + userName + "]"; } }
DataStoreTestServlet.java
import java.io.IOException; import java.util.List; import javax.jdo.PersistenceManager; import javax.jdo.Query; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class DataStoreTestServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { StringBuffer sb = new StringBuffer(); sb.append("Created users:\n"); for (int i = 0; i < 10; i++) { String uniqueName = "user" + System.currentTimeMillis(); User user = new User(uniqueName, "password"); save(user); User userFromDS = null; long startTime = System.currentTimeMillis(); long loop = 0; while (userFromDS == null) { userFromDS = get(uniqueName); loop++; if (userFromDS != null) { long endTime = System.currentTimeMillis(); sb.append(userFromDS.toString() + " took " + (endTime - startTime) + "ms, " + loop + " loop(s)\n"); } } } resp.setContentType("text/plain"); resp.getWriter().println(sb.toString()); } public Object save(Object obj) { PersistenceManager pm = PMF.get().getPersistenceManager(); Object savedObject = null; try { savedObject = pm.makePersistent(obj); } finally { pm.close(); } return savedObject; } public User get(String userName) { User user = null; List<User> users = null; PersistenceManager pm = PMF.get().getPersistenceManager(); Query query = pm.newQuery(User.class); query.setFilter("userName == nameParam"); query.declareParameters("String nameParam"); try { users = (List<User>) query.execute(userName); if (users != null && users.size() > 0) { user = users.get(0); } } finally { query.closeAll(); pm.close(); } return user; } }