Java GAE上带游标的JDO查询返回不一致的结果

Java GAE上带游标的JDO查询返回不一致的结果,java,google-app-engine,gwt,jdo,Java,Google App Engine,Gwt,Jdo,我正在运行对GAE服务器的GWT RPC调用,查询使用JDO存储在数据存储中的项目对象,并使用游标对结果进行分页 我发送了一个初始RPC调用,开始分页,结果的“范围”为10。我将查询游标存储在memcache中,并在用户请求下一页(共10个结果)时检索它。实现此功能的代码如下所示 范围总是相同的,10个结果。但是,一些后续RPC调用返回2个或12个结果。这是非常不一致的。调用有时还返回重复的结果 我已经阅读了谷歌开发者文档:。它提到:“对于对具有多个值的属性使用不等筛选器或排序顺序的查询,游标并

我正在运行对GAE服务器的GWT RPC调用,查询使用JDO存储在数据存储中的项目对象,并使用游标对结果进行分页

我发送了一个初始RPC调用,开始分页,结果的“范围”为10。我将查询游标存储在memcache中,并在用户请求下一页(共10个结果)时检索它。实现此功能的代码如下所示

范围总是相同的,10个结果。但是,一些后续RPC调用返回2个或12个结果。这是非常不一致的。调用有时还返回重复的结果

我已经阅读了谷歌开发者文档:。它提到:“对于对具有多个值的属性使用不等筛选器或排序顺序的查询,游标并不总是像预期的那样工作。这种多值属性的重复数据消除逻辑在两次检索之间不存在,可能会导致同一结果多次返回。”

正如您在代码中看到的,我正在按“日期”属性进行排序。此属性只有一个值

你能让我看看我做错了什么吗。谢谢

这是在GAE服务器上执行RPC调用的代码:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.HttpSession;

import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.datanucleus.query.JDOCursorHelper;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

//...

private void getTagArticles(String tag, int range, boolean start) {

    PersistenceManager pm = PMF.getNonTxnPm();
    ArticleStreamItemSummaryDTO aDTO = null;
    ArticleStreamItem aDetached = null;
    summaryList = new ArrayList<ArticleStreamItemSummaryDTO>();
    String cursorString = null;
    session = getThreadLocalRequest().getSession();
    UserAccount currentUser = LoginHelper.getLoggedInUser(session, pm);
    String cursorID = currentUser.getId().toString() + tag;

    if (start) { // The start or restart of the query
        CacheSupport.cacheDelete(String.class.getName(), cursorID);
    }

    Object o = CacheSupport.cacheGet(String.class.getName(), cursorID);
    if (o != null && o instanceof String) {
        cursorString = (String) o;
    }

    Query q = null;
    try {
        q = pm.newQuery(ArticleStreamItem.class);

        if (cursorString != null) {
            Cursor cursor = Cursor.fromWebSafeString(cursorString);
            Map<String, Object> extensionMap = new HashMap<String, Object>();
            extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor);
            q.setExtensions(extensionMap);
        }   
        q.setFilter("tag == tagParam");
        q.declareParameters("String tagParam");
        q.setOrdering("date desc");
        q.setRange(0, range);

        @SuppressWarnings("unchecked")
        List<ArticleStreamItem> articleStreamList = (List<ArticleStreamItem>) q.execute(tag);

        if (articleStreamList.iterator().hasNext()) {
            Cursor cursor = JDOCursorHelper.getCursor(articleStreamList);
            cursorString = cursor.toWebSafeString();
            CacheSupport.cacheDelete(String.class.getName(), cursorID);
            CacheSupport.cachePutExp(String.class.getName(), cursorID, cursorString, CACHE_EXPIR);
            for (ArticleStreamItem a : articleStreamList) {
                aDetached = pm.detachCopy(a);
                aDTO = aDetached.buildSummaryItem();
                summaryList.add(aDTO);
            }
        }
    }
    catch (Exception e) {
        // e.printStackTrace();
        logger.warning(e.getMessage());
    }
    finally {
        q.closeAll();
        pm.close();
    }
}
import java.util.ArrayList;
导入java.util.HashMap;
导入java.util.Iterator;
导入java.util.Map;
导入java.util.List;
导入java.util.logging.Logger;
导入javax.jdo.PersistenceManager;
导入javax.jdo.Query;
导入javax.servlet.http.HttpSession;
导入com.google.appengine.api.datastore.Cursor;
导入com.google.appengine.datanucleus.query.JDOCursorHelper;
导入com.google.gwt.user.server.rpc.RemoteServiceServlet;
//...
私有void getTagArticles(字符串标记、int范围、布尔开始){
PersistenceManager pm=PMF.getNonTxnPm();
ArticleStreamItemSummaryDTO aDTO=null;
ArticleStreamItem aDetached=null;
summaryList=新的ArrayList();
字符串游标字符串=null;
会话=getThreadLocalRequest().getSession();
UserAccount currentUser=LoginHelper.getLoggedInUser(会话,pm);
字符串cursorID=currentUser.getId().toString()+标记;
if(start){//查询的开始或重新启动
CacheSupport.cacheDelete(String.class.getName(),cursorID);
}
对象o=CacheSupport.cacheGet(String.class.getName(),cursorID);
if(o!=null&&o字符串实例){
游标字符串=(字符串)o;
}
查询q=null;
试一试{
q=pm.newQuery(ArticleStreamItem.class);
if(游标字符串!=null){
Cursor Cursor=Cursor.fromWebSafeString(cursorString);
Map extensionMap=newhashmap();
extensionMap.put(JDOCursorHelper.CURSOR\u扩展名,CURSOR);
q、 setExtensions(extensionMap);
}   
q、 setFilter(“tag==tagParam”);
q、 申报参数(“字符串参数”);
q、 设定订购(“日期描述”);
q、 设置范围(0,范围);
@抑制警告(“未选中”)
List articleStreamList=(List)q.execute(tag);
if(articleStreamList.iterator().hasNext()){
Cursor Cursor=JDOCursorHelper.getCursor(articleStreamList);
cursorString=cursor.towerbsafesting();
CacheSupport.cacheDelete(String.class.getName(),cursorID);
CacheSupport.cachePutExp(String.class.getName(),cursorID,cursorString,CACHE\u expire);
对于(ArticleStreamItem a:articleStreamList){
a附件=pm.副本(a);
aDTO=adattached.buildSummaryItem();
summaryList.add(aDTO);
}
}
}
捕获(例外e){
//e.printStackTrace();
logger.warning(例如getMessage());
}
最后{
q、 closeAll();
pm.close();
}
}

我在上述问题中提供的代码片段实际上运行良好。问题源于客户端。RPC调用有时彼此间隔几毫秒,这造成了我在返回的结果中看到的不一致行为

我更改了客户端代码,每5秒进行一次RPC调用,这就解决了这个问题