ドキュメントを読みつつ、すごく簡単なサンプルを書いてみた。
http://code.google.com/intl/en/appengine/docs/java/javadoc/com/google/appengine/api/datastore/package-summary.html
- web.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> <servlet> <servlet-name>LowLevelAPI</servlet-name> <servlet-class>hoge.fuga.piyo.LowLevelAPIServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LowLevelAPI</servlet-name> <url-pattern>/lowlevelapi</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
- LowLevelAPIServlet.java
package hoge.fuga.piyo; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; @SuppressWarnings("serial") public class LowLevelAPIServlet extends HttpServlet { private enum Create { hoge, fuga, } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String create = req.getParameter("create"); switch(Create.valueOf(create)) { case hoge: createHoge(); break; case fuga: createFuga(req.getParameter("hoge")); break; } resp.sendRedirect("/"); } private void createHoge() { DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService(); Entity entity = new Entity("Hoge"); datastoreService.put(entity); } private void createFuga(String hogeId) { DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService(); Key hogeKey = KeyFactory.createKey("Hoge", Long.parseLong(hogeId)); // hogeKeyの子としてエンティティを生成 Entity fuga = new Entity("Fuga", hogeKey); datastoreService.put(fuga); } }
- index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.google.appengine.api.datastore.Query" %> <%@ page import="com.google.appengine.api.datastore.DatastoreService" %> <%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %> <%@ page import="com.google.appengine.api.datastore.Entity" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService(); %> <dl> <% Query hogeQuery = new Query("Hoge"); Iterable<Entity> hogeIter = datastoreService.prepare(hogeQuery).asIterable(); for (Entity hoge : hogeIter) { %> <dt><%= hoge.getKey() %></dt> <% Query fugaQuery = new Query("Fuga", hoge.getKey()); Iterable<Entity> fugaIter = datastoreService.prepare(fugaQuery).asIterable(); for (Entity fuga : fugaIter) { %> <dd><%= fuga.getKey() %></dd> <% } } %> </dl> <form action="/lowlevelapi" method="post"> <input type="hidden" name="create" value="hoge"> <input type="submit" value="hoge"> </form> <% if (hogeIter.iterator().hasNext()) { %> <form action="/lowlevelapi" method="post"> <input type="hidden" name="create" value="fuga"> <select name="hoge"> <% for (Entity hoge : hogeIter) { %> <option value="<%= hoge.getKey().getId() %>"><%= hoge.getKey() %></option> <% } %> </select> <input type="submit" value="fuga"> </form> <% } %> </body> </html>
"hoge"ボタンを押すと、"Hoge"というkindのEntityが生成される。
これは普通にEntityのコンストラクタにkind名を渡して、DatastoreServiceからputするだけ。
"fuga"ボタンは、セレクトボックスで選択したHogeエンティティの子としてFugaエンティティを生成する。
Entity(java.lang.String kind, Key parent)
Create a new Entity with the specified kind and parent Entity.
とあるように、Entityクラスのコンストラクタの第1引数にkind名、第2引数にKeyを与えてやると、Keyを親とする子エンティティとしてEntityインスタンスが作られるらしい。
Python版でparentを指定してmodelインスタンスを作るときのイメージと似ていて、分かりやすい。
で、次にQuery。JSPで書いてみたらごちゃごちゃして読みにくくなったけど。。。
普通のQueryは、コンストラクタにkind名を渡してインスタンスを生成。そいつをDatastoreServiceのprepareメソッドに渡してやるとPreparedQueryが返ってくるので、そこからasIterable()なりasIterator()なりをもらってEntityをなめていく、ということになるようだ。
DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService(); Query hogeQuery = new Query("Hoge"); Iterable<Entity> hogeIter = datastoreService.prepare(hogeQuery).asIterable(); for (Entity hoge : hogeIter) { ... }
というカンジで。
JDOのQueryと違って、datastoreのQueryはancestorを指定したQueryを生成できる。
Query(java.lang.String kind, Key ancestor)
Create a new Query that finds Entity objects with the specified kind.
GAE/J でancestorによるフィルタリングはできない? - すぎゃーんメモでつまづいた問題も、このLow-level APIを使えば解決できる。
Query fugaQuery = new Query("Fuga", hoge.getKey()); Iterable<Entity> fugaIter = datastoreService.prepare(fugaQuery).asIterable(); for (Entity fuga : fugaIter) { ... }
こうすることで、hogeから得たKeyを親として持つFugaのエンティティを取得することができた。
感想など
Python版に近い感覚でDatastoreを扱うことができるような気がする。
propertyのget/setが大変そう。基本的に
setProperty(java.lang.String propertyName, java.lang.Object value)
でセットして、
getProperty(java.lang.String propertyName)
でゲットすることになるらしい。
DataTypeTranslatorってのを使うと型変換とかをうまくやってくれるんだろうか?まだ使ってみていないので分からない。
あと、上記のサンプルを試しに本番デプロイしてみたところ、DashboardのData Viewerから見ることができなかった。Query書いてみてもServer Errorになってしまったし。。Low-level APIで保存したデータはData Viewerから見られないのかな?それとも何かやり方を間違っているのだろうか?
追記。時間経ってから確認したらちゃんと見られるようになっていた。DataViewerに反映されるまで少し時間がかかるだけかも。