查询游标让应用以便捷的批量方式检索查询结果,而且不会因查询偏移而产生开销。执行检索操作后,应用会获得一个游标,该游标是一个不透明的 base64 编码字符串,用于标记上次检索的结果的索引位置。应用可以保存此字符串(例如,保存到 Datastore、Memcache、任务队列的任务载荷中,或者以 HTTP GET 或 POST 参数形式嵌入网页中),然后可使用游标作为起点来执行后续检索操作,以从上一次检索结束的位置获取下一批结果。检索还可以指定结束游标,以限制所返回的结果集的范围。
importcom.google.appengine.api.datastore.Cursor;importcom.google.appengine.api.datastore.DatastoreService;importcom.google.appengine.api.datastore.DatastoreServiceFactory;importcom.google.appengine.api.datastore.Entity;importcom.google.appengine.api.datastore.FetchOptions;importcom.google.appengine.api.datastore.PreparedQuery;importcom.google.appengine.api.datastore.Query;importcom.google.appengine.api.datastore.Query.SortDirection;importcom.google.appengine.api.datastore.QueryResultList;importjava.io.IOException;importjava.io.PrintWriter;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;publicclassListPeopleServletextendsHttpServlet{staticfinalintPAGE_SIZE=15;privatefinalDatastoreServicedatastore;publicListPeopleServlet(){datastore=DatastoreServiceFactory.getDatastoreService();}@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{FetchOptionsfetchOptions=FetchOptions.Builder.withLimit(PAGE_SIZE);// If this servlet is passed a cursor parameter, let's use it.StringstartCursor=req.getParameter("cursor");if(startCursor!=null){fetchOptions.startCursor(Cursor.fromWebSafeString(startCursor));}Queryq=newQuery("Person").addSort("name",SortDirection.ASCENDING);PreparedQuerypq=datastore.prepare(q);QueryResultList<Entity>results;try{results=pq.asQueryResultList(fetchOptions);}catch(IllegalArgumentExceptione){// IllegalArgumentException happens when an invalid cursor is used.// A user could have manually entered a bad cursor in the URL or there// may have been an internal implementation detail change in App Engine.// Redirect to the page without the cursor parameter to show something// rather than an error.resp.sendRedirect("/people");return;}resp.setContentType("text/html");resp.setCharacterEncoding("UTF-8");PrintWriterw=resp.getWriter();w.println("<!DOCTYPE html>");w.println("<meta charset=\"utf-8\">");w.println("<title>Cloud Datastore Cursor Sample</title>");w.println("<ul>");for(Entityentity:results){w.println("<li>"+entity.getProperty("name")+"</li>");}w.println("</ul>");StringcursorString=results.getCursor().toWebSafeString();// This servlet lives at '/people'.w.println("<a href='/people?cursor="+cursorString+"'>Next page</a>");}}
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["很难理解","hardToUnderstand","thumb-down"],["信息或示例代码不正确","incorrectInformationOrSampleCode","thumb-down"],["没有我需要的信息/示例","missingTheInformationSamplesINeed","thumb-down"],["翻译问题","translationIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-20。"],[[["\u003cp\u003eThis API supports first-generation runtimes and can be used when upgrading to corresponding second-generation runtimes, with a migration guide available for App Engine Java 11/17 users.\u003c/p\u003e\n"],["\u003cp\u003eQuery cursors are recommended over integer offsets for pagination, allowing applications to retrieve query results in batches without the overhead of a query offset.\u003c/p\u003e\n"],["\u003cp\u003eCursors are opaque base64-encoded strings marking the index position of the last retrieved result, enabling applications to save and use them for subsequent retrievals.\u003c/p\u003e\n"],["\u003cp\u003eUnlike offsets, using cursors avoids the cost of retrieving and processing skipped entities, as these entities are still retrieved internally when offsets are used.\u003c/p\u003e\n"],["\u003cp\u003eCursors have limitations, including the requirement to use the exact same query, restrictions with certain operators, and potential invalidation due to App Engine updates, which could raise errors.\u003c/p\u003e\n"]]],[],null,[]]