Exception: Query result sets are not modifiable
i saw a similar issue here but it did not answer my question so i decided to start a new one.
I am trying to work around the consistently getting inconsistent reads issue ( https://groups.google.com/forum/?fromgroups=#!topic/google-appengine/E95wH0RkJUE ) with the High Replication datastore of Google App Engine by temporarily storing the last added object in a static variable.
Whenever the user adds a new flight, instead of just directly persisting the new flight object, it store it in PMF as a static variable.
So when the list of flights is fetched in a query, it stores the result it the list, and the list is compared with the last added object, if the last added object is not in the list, i manually add the last added object to the list.
However using this method, i get the following exception whenever the newly added object is missing from the list and i manually add it.
Oct 6, 2012 2:19:38 PM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: /
java.lang.UnsupportedOperationException: Query result sets are not modifiable
at org.datanucleus.store.query.AbstractQueryResult.add(AbstractQueryResult.java:222)
at com.jimfoo88.helloorm.GetFlights.queryJDO(GetFlights.java:101)
at com.jimfoo88.helloorm.GetFlights.doGet(GetFlights.java:50)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:35)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:60)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:370)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
My code below is based on the google app engine sample, HelloORM http://code.google.com/p/datanucleus-appengine/source/browse/trunk/demos/helloorm/src/com/google/appengine/demos/helloorm/GetFlights.java?r=736
I only show parts that i modify to simplify reading.
AddFlight.java
private void doPostJDO(Flight f) {
PersistenceManager pm = PMF.get().getPersistenceManager();
PMF.setLastObjectAdded(f);
try {
pm.makePersistent(f);
} finally {
pm.close();
}
}
PMF.java
// Copyright 2008 Google Inc. All Rights Reserved.
package com.jimfoo88.helloorm;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
/**
* @author Max Ross <maxr@google.com>
*/
public final class PMF {
private static Object lastObjectAdded;
private static final PersistenceManagerFactory INSTANCE = JDOHelper.getPersistenceManagerFactory("transactions-optional");
public static Object getLastObjectAdded() {
return lastObjectAdded;
}
public static void setLastObjectAdded(Object lastObject) {
lastObjectAdded = lastObject;
}
public static PersistenceManagerFactory get() {
return INSTANCE;
}
private PMF() {}
}
GetFlights.java
private Collection<Flight> queryJDO(String query) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
List<Flight> flights = (List<Flight>) pm.newQuery(query).execute();
// Force all results to be pulled back before we close the entity
// manager.
// We could have also called pm.detachCopyAll()
flights.size();
//pm.detachCopyAll(flights);
boolean found = false;
try {
for (Flight f : flights) {
if ((Flight) PMF.getLastObjectAdded() == null) {
found = true;
break;
}
if (f.getDest() == ((Flight) PMF.getLastObjectAdded())
.getDest()
&& f.getOrig() == ((Flight) PMF
.getLastObjectAdded()).getOrig()) {
logger.info("found object " + f.toString()
+ "in datastore");
found = true;
break;
}
}
if (found == false) {
logger.info("did not find object "
+ (Flight) PMF.getLastObjectAdded()
+ " in datastore, need to add it");
flights.add((Flight) PMF.getLastObjectAdded());
}
} catch (NullPointerException e) {
logger.error(e.getMessage());
}
return flights;
} finally {
pm.close();
}
}
Will appreciate if anybody could point out my mistakes. If there is any better way to work around this issue, please let me know. Thanks.
So query results are not modifiable, so don’t attempt to add objects to them. I don’t see anything even slightly incorrect/inconsistent about that (and is in fact what the JDO/JPA specs say)
If you want to make use of those results in a later context you can easily create a new List, do an addAll() with the query results, and use that List