データストアからデータを取得する
プロジェクトの作成
以下の設定でGWTプロジェクトを作成してください。GWTプロジェクトの作成手順については、第1回の解説を参照してください。今回は日本語(2バイトコード)を扱いたいので、言語設定は必ず[UTF-8]に設定してください。
| Project name | ShowGrap |
| Package | com.daisukeyamashita.test.jdo |
ライブラリの追加
データは、Google Visualization APIを利用して、グラフで表示します。
まず、GWT用のライブラリをダウンロードしてください。アーカイブファイルの中にある「gwt-visualization.jar」ファイルをプロジェクト直下にコピーして、プロジェクトのライブラリパスに追加してください。
このライブラリはGWTのコンパイル時にのみ利用するので、「WEB-INF/lib」にコピーする必要はありません。
プログラムの作成
まず、「src/com/daisukeyamashita/test/jdo/ShowGrap.gwt.xml」の<module>タグの直後(4行目)に<inherits name="com.google.gwt.visualization.Visualization" />を挿入してください。
PMFクラスは先ほどと同じです。
package com.daisukeyamashita.test.jdo.server;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public class PMF {
private static final PersistenceManagerFactory pmfInstance = JDOHelper
.getPersistenceManagerFactory("transactions-optional");
private PMF() {
}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
}
StoreDataクラスは先ほどのクラスに、Getterメソッドを追加したものです。Datastore Java APIでは、Getter/Setterメソッドは必要なければ定義する必要がありません。
package com.daisukeyamashita.test.jdo.server;
import javax.jdo.annotations.IdGeneratorStrategy;
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 StoreData {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private String name;
@Persistent
private Integer data;
public StoreData(String name, Integer data) {
this.name = name;
this.data = data;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Integer getData() {
return data;
}
}
ChartDataクラスはGWTのAjax通信で送受信するためのシリアライズクラスです。シリアライズクラスであることをGWTコンパイラに通知するために、com.google.gwt.user.client.rpc.IsSerializableインターフェイスをimplementsしておく必要があります。
package com.daisukeyamashita.test.jdo.client;
import com.google.gwt.user.client.rpc.IsSerializable;
public class ChartData implements IsSerializable {
private String name;
private Integer num;
public ChartData() {
}
public ChartData(String name, Integer num) {
this.name = name;
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
GWTのAjax通信用のクライアントサイドのソースコードを定義します。
package com.daisukeyamashita.test.jdo.client;
import java.util.List;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
/**
* The client side stub for the RPC service.
*/
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
List<ChartData> getList(String name);
}
package com.daisukeyamashita.test.jdo.client;
import java.util.List;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* The async counterpart of <code>GreetingService</code>.
*/
public interface GreetingServiceAsync {
void getList(String name, AsyncCallback<List<ChartData>> callback);
}
GWTのAjax通信のサーバサイド部分のソースコードを定義します。ここで、データストアからデータを引き出しています。
package com.daisukeyamashita.test.jdo.server;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.jdo.PersistenceManager;
import com.daisukeyamashita.test.jdo.client.ChartData;
import com.daisukeyamashita.test.jdo.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
* The server side implementation of the RPC service.
*/
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
private Logger logger = Logger.getLogger(GreetingServiceImpl.class.toString());
@SuppressWarnings("unchecked")
public List<ChartData> getList(String name) {
PersistenceManager pm = PMF.get().getPersistenceManager();
String query = "select from " + StoreData.class.getName();
logger.info("query = " + query);
List<StoreData> list = (List<StoreData>) pm.newQuery(query).execute();
ArrayList<ChartData> listChartData = new ArrayList<ChartData>();
for (StoreData storedData : list) {
String nameRandom = storedData.getName();
logger.info("name = " + nameRandom);
Integer numRandom = storedData.getData();
logger.info("num = " + numRandom);
listChartData.add(new ChartData(nameRandom, numRandom));
}
return listChartData;
}
}
クライアント部分のソースコードを定義します。
package com.daisukeyamashita.test.jdo.client;
import java.util.HashMap;
import java.util.List;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
import com.google.gwt.visualization.client.visualizations.PieChart;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class ShowGrap implements EntryPoint {
/**
* Create a remote service proxy to talk to the server-side Greeting
* service.
*/
private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
private Widget widget = new Label("データを表示します");
/**
* This is the entry point method.
*/
public void onModuleLoad() {
// final TextBox nameField = new TextBox();
// nameField.setText("GWT User");
final AsyncCallback<List<ChartData>> callback = new AsyncCallback<List<ChartData>>() {
public void onSuccess(List<ChartData> result) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
for (ChartData random : result) {
String name = random.getName();
Integer num = map.get(name);
if (num == null) {
num = new Integer(0);
}
num += random.getNum();
map.put(name, num);
}
DataTable data = DataTable.create();
data.addColumn(ColumnType.STRING, "Task");
data.addColumn(ColumnType.NUMBER, "Hours per Day");
data.addRows(map.size());
int count = 0;
for (String name : map.keySet()) {
data.setValue(count, 0, name);
data.setValue(count, 1, map.get(name));
count++;
}
/* create pie chart */
PieChart.Options options = PieChart.Options.create();
options.setWidth(400);
options.setHeight(240);
options.set3D(true);
options.setTitle("GAEのデータ");
RootPanel.get("nameFieldContainer").remove(widget);
widget = new PieChart(data, options);
RootPanel.get("nameFieldContainer").add(widget);
}
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
};
final Button btnGet = new Button("Get!");
btnGet.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
greetingService.getList("", callback);
}
});
RootPanel.get("nameFieldContainer").add(widget);
RootPanel.get("sendButtonContainer").add(btnGet);
}
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set -->
<!-- the browser's rendering engine into -->
<!-- "Quirks Mode". Replacing this declaration -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout. -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- -->
<!-- Consider inlining CSS to reduce the number of requested files -->
<!-- -->
<link type="text/css" rel="stylesheet" href="ShowGrap.css">
<!-- -->
<!-- Any title is fine -->
<!-- -->
<title>Web Application Starter Project</title>
<!-- -->
<!-- This script loads your compiled module. -->
<!-- If you add any GWT meta tags, they must -->
<!-- be added before this line. -->
<!-- -->
<script type="text/javascript" language="javascript" src="showgrap/showgrap.nocache.js"></script>
<script type="text/javascript" src="http://www.google.com/jsapi"></script> </head>
<!-- -->
<!-- The body can have arbitrary html, or -->
<!-- you can leave the body empty if you want -->
<!-- to create a completely dynamic UI. -->
<!-- -->
<body>
<!-- OPTIONAL: include this if you want history support -->
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<script type="text/javascript">
google.load("visualization", "1", {'packages' : ["piechart", "table"] });
</script>
<h1>Web Application Starter Project</h1>
<table align="center">
<tr>
<td id="nameFieldContainer"></td>
<td id="sendButtonContainer"></td>
</tr>
</table>
</body>
</html>
実行する
これまでの手順で、Datastore Java APIを利用したプログラムが完成しました。プロジェクト名を右クリックして、[Run As]-[Web Application]を選択してください。
次のように表示されていればOKです。[Get!]ボタンをクリックします。

ローカル環境にはデータが保存されていませんので、次のように[No data]と表示されているのを確認してください。

ローカル環境で正常に動作することが確認できたら、アプリケーションをGoogle App Engineにデプロイしてください。先ほどのアプリケーションで保存したデータを確認したいので、先ほどのアプリケーションと同じ場所にデプロイしてください。
デプロイが完了したら、[Get!]ボタンを押してデータを表示してみてください。次のようなグラフが表示されると思います。

円グラフの一部をクリックするとデータの内容を確認することができます。

まとめ
今回はDatastore Java APIを勉強しましたが、いかがでしたでしょうか? 今回の記事で、Javaからデータストアを簡単に操作できることが分かっていただけたかと思います。
今回はDatastore Java APIのさわりしか勉強していません。Datastore Java APIでは、もっと高度な制御ができますので、興味のある方はGoogle App Engineのドキュメントを参考に勉強されることをお勧めします。
この連載でも機会があれば、Datastore Java APIの高度な利用方法を書きたいと思います。
