ActiveObjectsでたったの2時間で作れる3次元グラフ
ついカッとなってやった、ActiveObjectsのいいところを見せられれば何でもよかった。
2時間とは
ここから
ここまで
そろそろActiveObjectsで本気だす!!!
canvas3DGraph.js+prototype.js+Wicket+ActiveObjects+MySQLで、3Dグラフ表示&更新プログラムを作った。
画面&データ
左側のブラウザでグラフデータの追加をして、右側のデータで自動的に3次元グラフの描画をしてる。
ちょっとずつデータを追加していく様子の、画面とデータを張り付けてく。
初期データ
mysql> select * from threeDData; +----+------+------+------+ | id | z | x | y | +----+------+------+------+ | 1 | 30 | 50 | 40 | | 2 | 80 | 120 | 70 | | 3 | 100 | 100 | 100 | | 4 | 500 | 500 | 500 | | 5 | 400 | 300 | 400 | +----+------+------+------+ 5 rows in set (0.00 sec)
データ追加1
mysql> select * from threeDData; +----+------+------+------+ | id | z | x | y | +----+------+------+------+ | 1 | 30 | 50 | 40 | | 2 | 80 | 120 | 70 | | 3 | 100 | 100 | 100 | | 4 | 500 | 500 | 500 | | 5 | 400 | 300 | 400 | | 6 | 100 | 400 | 300 | +----+------+------+------+ 6 rows in set (0.00 sec)
データ追加2
mysql> select * from threeDData; +----+------+------+------+ | id | z | x | y | +----+------+------+------+ | 1 | 30 | 50 | 40 | | 2 | 80 | 120 | 70 | | 3 | 100 | 100 | 100 | | 4 | 500 | 500 | 500 | | 5 | 400 | 300 | 400 | | 6 | 100 | 400 | 300 | | 7 | 800 | 800 | 800 | +----+------+------+------+ 7 rows in set (0.00 sec)
データ追加3
mysql> select * from threeDData; +----+------+------+------+ | id | z | x | y | +----+------+------+------+ | 1 | 30 | 50 | 40 | | 2 | 80 | 120 | 70 | | 3 | 100 | 100 | 100 | | 4 | 500 | 500 | 500 | | 5 | 400 | 300 | 400 | | 6 | 100 | 400 | 300 | | 7 | 800 | 800 | 800 | | 8 | 200 | 800 | 300 | +----+------+------+------+ 8 rows in set (0.00 sec)
ソース
データ追加画面 - AddDataPage.html
<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.sourceforge.net/"> <head> <title>データの追加</title> </head> <body> <h1>データの追加</h1> <div wicket:id="message">メッセージ</div> <form wicket:id="addForm"> X: <input type="text" wicket:id="x"/><br/> Y: <input type="text" wicket:id="y"/><br/> Z: <input type="text" wicket:id="z"/><br/> <input type="submit" wicket:id="add" value="追加" /> </form> </body> </html>
データの追加画面 - AddDataPage.java
import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.Model; import net.java.ao.EntityManager; import java.sql.SQLException; /** * データの追加 */ public class AddDataPage extends WebPage { /** * コンストラクタ。 */ public AddDataPage() { final Label label = new Label("message", new Model("データを追加できます。")); this.add(label); Form addForm = new Form("addForm"); final TextField x = new TextField("x", new Model("0.0")); final TextField y = new TextField("y", new Model("0.0")); final TextField z = new TextField("z", new Model("0.0")); addForm.add(x); addForm.add(y); addForm.add(z); addForm.add(new Button("add") { public void onSubmit() { Double xValue = Double.valueOf((String)x.getModel().getObject()); Double yValue = Double.valueOf((String)y.getModel().getObject()); Double zValue = Double.valueOf((String)z.getModel().getObject()); EntityManager manager = new EntityManager( "jdbc:mysql://localhost/aotest", "root", null ); java.util.logging.Logger.getLogger("net.java.ao").setLevel(java.util.logging.Level.FINE); try { ThreeDData data = manager.create( ThreeDData.class ); data.setX( xValue ); data.setY( yValue ); data.setZ( zValue ); data.save(); } catch ( SQLException e ) { throw new RuntimeException( e ); } setResponsePage( new AddDataPage() ); } }); this.add(addForm); } }
グラフの画面 - ThreeDGraph.html
<html> <head> <title>3次元グラフ</title> <script type="text/javascript" src="prototype.js"></script> <script type="text/javascript" src="excanvas.js"></script> <script type="text/javascript" src="canvas3DGraph.js"></script> <script type="text/javascript"> /** * 3Dグラフを表示する。 */ function show3DGraph() { // データを取得するために、サーバにリクエストを出す。 var myAjax = new Ajax.Request( 'http://192.168.1.21:8080/wicket/', { method: 'get', parameters: 'wicket:bookmarkablePage=:ThreeDJson', onComplete: show3DGraph_cb } ); } /** * 3Dグラフを表示する。(サーバからのコールバック後の処理) */ function show3DGraph_cb( req ) { // サーバのレスポンスからデータを取得する。 var json = req.responseText.replace( /<span[^>]*>/, "" ).replace( /<\/span>/, "" ).replace( /\r/, "" ).replace( /\n/, "" ); var data = eval( json ); // graphオブジェクトを取得する。 var g = new canvasGraph( 'graph' ); // データを並べ替える。 data.sort( sortNumByZ ); // グラフを描く。 g.drawGraph( data ); } // 画面ロード時に実行する。 Event.observe( window, "load", function(){ // 3Dグラフの実行を一定時間毎に行うように、タイマーを設定する。 setInterval( show3DGraph, 1000 ); }); </script> <style type="text/css"> #g-holder { height:620px; position:relative; } #canvasDiv{ border:solid 1px #e1e1e1; width:600px; height:600px; position:absolute; top:0px; left:0px; z-index:10; } #x-label{ position:absolute; z-index:2; top:340px; left:580px; } #y-label{ position:absolute; z-index:2; top:10px; left:220px; } #z-label{ position:absolute; z-index:2; top:540px; left:10px; } #gInfo div.gText{ position:absolute; z-index:-1; font:normal 10px Arial; } </style> </head> <body> <div id="g-holder"> <div id="canvasDiv"> <canvas id="graph" width="600" height="600" ></canvas> <div id="gInfo"></div> </div> <div id="controls"> <!-- (put your controls here, if you need any) --> </div> </div> </body> </html>
グラフの画面 - ThreeDGraph.java
import net.java.ao.EntityManager; import java.sql.SQLException; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; public class ThreeDGraph extends WebPage { }
グラフデータ取得処理(Ajax) - ThreeDJson.html
<span wicket:id="json"/>
グラフデータ取得処理(Ajax) - ThreeDJson.java
import net.java.ao.EntityManager; import java.sql.SQLException; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; public class ThreeDJson extends WebPage { public ThreeDJson() { ThreeDData[] data; try { EntityManager manager = new EntityManager( "jdbc:mysql://localhost/aotest", "root", "" ); data = manager.find( ThreeDData.class ); } catch ( SQLException e ) { throw new RuntimeException( e ); } StringBuilder json = new StringBuilder(); json.append( "([" ); for ( int i = 0; i < data.length; ++i ) { if ( i > 0 ) json.append( "," ); json.append( "{x:" + data[i].getX() + ",y:" + data[i].getY() + ",z:" + data[i].getZ() + "}" ); } json.append( "])" ); add( new Label( "json", json.toString() ) ); } }
データのエンティティ - ThreeDData.java
AOのエンティティにとって、主キーなんて大事じゃありません。
import net.java.ao.Entity; public interface ThreeDData extends Entity { /* * X座標 */ Double getX(); void setX( Double x ); /* * Y座標 */ Double getY(); void setY( Double y ); /* * Z座標 */ Double getZ(); void setZ( Double z ); }
マイグレーションだってできちゃうよ
マイグレーションすればエンティティからテーブルつくってくれるです。
マイグレーション用ソース - Migration.java
import java.sql.SQLException; import net.java.ao.EntityManager; public class Migration { public static void main( String[] args ) { EntityManager manager = new EntityManager( "jdbc:mysql://localhost/aotest", "root", null ); java.util.logging.Logger.getLogger("net.java.ao").setLevel(java.util.logging.Level.FINE); try { manager.migrate( ThreeDData.class ); } catch ( SQLException e ) { throw new RuntimeException( e ); } } }
ビルドファイル - build.sh
ビルド時にマイグレーションしちゃうよ!
#!/bin/bash SRC_DIR=./src LIB_DIR=./wicket/WEB-INF/lib CLASSES_DIR=./wicket/WEB-INF/classes LIBS=$LIB_DIR/slf4j-api-1.5.6.jar LIBS=$LIBS:$LIB_DIR/slf4j-jdk14-1.5.6.jar LIBS=$LIBS:$LIB_DIR/wicket-1.3.5.jar LIBS=$LIBS:$LIB_DIR/activeobjects-0.8.2.jar LIBS=$LIBS:$LIB_DIR/mysql-connector-java-5.1.0-bin.jar SOURCES=$SRC_DIR/*.java HTMLS=$SRC_DIR/*.html WEBAPPS_DIR=/var/lib/tomcat6/webapps javac -classpath $CLASSES_DIR:$LIBS -d $CLASSES_DIR -sourcepath $SRC_DIR $SOURCES cp $HTMLS $CLASSES_DIR jar cvf wicket.war -C ./wicket . sudo cp -p wicket.war $WEBAPPS_DIR/ java -classpath $CLASSES_DIR:$LIBS Migration
気になったので言及
ActiveObjectsをやめた
・・・
なんか、0より大きく1より小さい場合以外はすべてExceptionがスローされるような実装です。というか、エラー内容からも明らかなように、プライマリキーがきっかり1つでなければ必ずエラーになります。プライマリキー指定はテーブルに関連があったりすると増えることもありますから、ちょっと不安です。
ActiveObjectsはjavaのインスタンスを丸ごとDBにマッピングしてくれるです。
インスタンスの丸ごとマッピングなので、主キーが1個とか2個とか関係ないです。
そう・・・主キーなんて大事じゃないんです。大事なことなので2度言いました。
むしろ、AOを使うならDBのことなんか忘れちゃえばいいんです。
って・・・ぜんぜん説明できていない・・・。
追記
ActiveObjectsをちゃんと知るには、こっちを読んだ方がいいと思う→図解、ActiveObjectsの一番大事なところ!