リスト・プロパティを含むエンティティの永続化
4. コンピュータ商品の参照
プロパティにリスト項目を含むエンティティの参照
4.1. コンピュータ商品マスター参照
それでは次に、参照画面について説明します。前回は、全件参照のサンプルでした。今回は、キー検索で商品データを表示します。
図11: コンピュータ商品参照画面 |
図11は、コンピュータ商品の参照画面です。この画面からすべての商品を参照できます。
この画面から製品番号にPC0001を入力して「参照」ボタンをクリックすると、図12のようなデスクトップPCの製品情報が表示されます。
図12: デスクトップPCの商品情報を参照 |
図12の参照画面では、デスクトップPCのスペック項目フィールドは、すべてセレクト・メニュー形式で表示されています。セレクト・メニューで表示される項目は、図13から図15のようにリスト形式プロパティとしてBigtableに登録されたプロパティ項目が対応しています。
図13: CPUのセレクト・メニュー |
図14: メモリーのセレクト・メニュー |
図15: OSのセレクト・メニュー |
デスクトップPCのほかに、液晶ディスプレイやプリンタについても、図11の参照画面から同様に検索表示され、それぞれ図16、図17のように表示されます。ここでのセレクト・メニューからの選択は、ユーザー用の製品購入画面のスペック選択用としても、そのまま使用することができるでしょう。
また、例えば、CPUの種類などを選択した時に、価格フィールドを自動的に変更するような機能追加も必要になるかもしれません。これらも、キー・バリュー・ストアの柔軟性とJavaScript処理を組み合わせれば容易に実現できます。ここでは、セレクト・メニュー表示がDWRのユーティリティによって1行コードとして表示されますが、内容についてはクライアント・コードで解説します。
図16: 液晶ディスプレイの参照表示 |
図17: プリンタの参照表示 |
4.2. 商品マスター参照サーバー側プログラム
【リスト3】: 商品マスター参照サーバー側プログラム(revItemメソッド)
: DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); //(1) : public String revItem(String itemNo) { //(2) String out = ""; try { Key itemNoKey = KeyFactory.createKey("itemMas2", itemNo); //(3) Entity imas = ds.get(itemNoKey); //(4) String type = imas.getProperty("type").toString() out += type+"<i>" ; out += imas.getProperty("itemName").toString()+"<i>"; out += imas.getProperty("price").toString()+"<i>"; if(type.equals("デスクトップPC")){ out += imas.getProperty("cpu").toString()+"<i>"; //(4) out += imas.getProperty("memory").toString()+"<i>"; //(4) out += imas.getProperty("os").toString()+"<i>"; //(4) }else if(type.equals("液晶ディスプレイ")){ out += imas.getProperty("size").toString()+"<i>"; }else if(type.equals("プリンタ")){ out += imas.getProperty("papersize").toString()+"<i>"; out += imas.getProperty("resolution").toString()+"<i>"; } out += "OK:商品番号: "+itemNo; return out; } catch(Exception e) { return "NO<i>:参照失敗 商品番号: "+itemNo; } } :
リスト3は、キー検索による参照画面のサンプルです。リスト・プロパティを含む場合でも、含まない場合と全く同じです。処理手順は、以下の通りです。
- (1)最初にメソッド共通で使われるDatastoreServiceのインスタンス「ds」は、事前に生成されています。
- (2)で、クライアントから送信されてきたキー項目(製品番号)を受け取ります。
- (3)のcreateKeyメソッドで、第1引数にテーブル(kind)名、第2引数にキー項目を指定して、検索用のキーを生成します。
- (4)次に、(1)で生成されたdsのgetメソッドを引数に、検索用キーを指定して実行すると、指定したキーを持つエンティティ(imas)が取得できます。
その後は、getPropertyメソッドで、順次プロパティ項目を取得していきます。リスト・プロパティを含む場合も、含まない場合と全く同じように取得できます。例えば、デスクトップPCのリスト・プロパティは(4)で取得していますが、リスト項目以外の場合と全く同じ処理になっています。
4.3. 商品マスター参照クライアント側プログラム
【リスト4】: 商品マスター参照クライアント側プログラム(revItem.htm)
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>商品マスタ登録</title> <link type="text/css" rel="stylesheet" href="/csslib/style.css"/> <script type="text/javascript" src="/dwr/interface/colBean.js"></script> <script type="text/javascript" src="/dwr/engine.js"></script> <script type="text/javascript" src="/dwr/util.js"></script> <script type="text/javascript" src="/jslib/jquery-1.4.2.min.js"></script> <script type="text/javascript"> //<![CDATA[ window.resizeTo(380,510); window.moveTo(10,10); var kind = "desktop"; var query = {}; var spectag1 = '<table width="330" border="1" bgcolor="#eeeeee">' + '<tr><th width="100">CPU</th>' + '<td><select id="cpu"><option value="">=CPU=</option></select></td></tr>' + '<tr><th>メモリ</th>' + '<td><select id="memory"><option value="">=メモリ=</option></select></td></tr>' + '<tr><th>OS</th>' + '<td><select id="os"><option value="">=OS=</option></select></td></tr>' + '<tr><th>ステータス</th><td id="status"></td></tr></table>'; var spectag2 = '<table width="330" border="1" bgcolor="#eeeeee">' + '<tr><th width="100">サイズ</th>' + '<td><select id="size"><option value="">=サイズ=</option></select></td></tr>' + '<tr><th>ステータス</th><td id="status"></td></tr></table>'; var spectag3 = '<table width="330" border="1" bgcolor="#eeeeee">' + '<tr><th width="100">用紙サイズ</th>' + '<td><select id="papersize"><option value="">用紙サイズ</option></select></td></tr>' + '<tr><th>解像度</th>' + '<td><select id="resolution"><option value="">解像度</option></select></td></tr>' + '<tr><th>ステータス</th><td id="status"></td></tr></table>'; $(function(){ $("#type").val("デスクトップPC"); $("#rev").click(function(e){ colBean.revItem($("#itemNo").val(), function(dat){ var items = dat.split("<i>"); var type = items[0]; $("#type").text(type); $("#itemName").text(items[1]); $("#price").text(items[2]); if(type=="デスクトップPC"){ $("#spec").html(spectag1); var cpu = items[3].replace("["," ").replace("]"," ").split(","); //(2) DWRUtil.addOptions("cpu", cpu); //(3) var memory = items[4].replace("["," ").replace("]"," ").split(","); DWRUtil.addOptions("memory", memory); var os = items[5].replace("["," ").replace("]"," ").split(","); DWRUtil.addOptions("os", os); }else if(type=="液晶ディスプレイ"){ $("#spec").html(spectag2); var size = items[3].replace("["," ").replace("]"," ").split(","); DWRUtil.addOptions("size", size); }else if(type=="プリンタ"){ $("#spec").html(spectag3); var papersize = items[3].replace("["," ").replace("]"," ").split(","); DWRUtil.addOptions("papersize", papersize); var resolution = items[4].replace("["," ").replace("]"," ").split(","); DWRUtil.addOptions("resolution", resolution); } }); }); }); //]]> </script> </head> <body bgcolor="#cccccc"> <h2 style="color: #aa0022">コンピュータ商品参照 <input type="button" id="rev" value=" 参照 "/> </h2> <table width="330" border="1" bgcolor="#eeeeee"> <tr><th id="stitle" colspan="2">デスクトップPC</th></tr> <tr> <th width="100">製品番号</th> <td><input type="text" size="12" id="itemNo"/></td> </tr> <tr><th>タイプ</th><td id="type"></td></tr> <tr><th>製品名</th><td id="itemName"></td></tr> <tr><th>価格</th><td id="price"></td></tr> </table> <nobr id="spec"></nobr> </body> </html>
クライアント側のプログラムも、第2回のサンプルとほとんど変わりません。製品ごとの選択項目をセレクト・メニューで表示するところだけが異なっています。DWRユーティリティを使用してセレクト・メニューにデータをセットする場合、データは配列にしておく必要があります。この処理はスマートではありませんが、例えばCPUの種類選択は、受信データ(items[3])を使って(2)で処理しています。
(2)では、(1)の/dwr/util.jsに含まれているメソッドを使用して、セレクト・メニューを表示しています。書式は以下の通りです。
DWRUtil.addOptions("ID値",配列);
第1引数には、間にセレクト・タグのID値を指定します。第2引数にオプション項目で表示する項目を配列で指定します。これにより、セレクト・メニューを表示できます。例えば、CPUの選択項目表示では、(3)のDWRUtil.addOptions("cpu", cpu);の記述で、セレクト・メニューが表示されます。
以上、今回は、リスト・プロパティについて説明しました。今回の内容も、RDBでは実現不可能な、テーブル構造の柔軟性を示すものです。また、この柔軟性をプログラム・レベルで反映する方法としては、データ・クラスの定義も含めて煩雑さを増してしまうJDOではなく、Low-Level APIの方が優っています。
次回は、App Engineデータ・ストアにおけるトランザクション処理について説明します。
App Engineでのトランザクションには大きな制約があります。それは、「複数エンティティを対象とするトランザクション処理では、それぞれのエンティティを所有/被所有の関係にしておかなければならない」というものです。つまり、単一エンティティを対象としたトランザクション処理には制約は課されませんが、複数エンティティの場合はエンティティ構成で制約が課されてしまう、ということです。
しかし、ここまで説明したように、Bigtableでのテーブル(kind)をスキーマ定義のあるRDBのテーブルとしてデザインした場合には、正規化によって相当数のテーブルに分けられることになるのは明白です。今回の内容に関係する部分だけでも、リスト・プロパティ項目はすべて、別テーブルとしてスキーマ設計することになります。
このように、Bigtableでのトランザクションに対する制約は、キー・バリュー・ストアのマイナス点としてとらえられることが多いですが、そもそも制約となる複数テーブル分割の必要性が大幅に減少するというプラス面を合わせて評価すべきものでしょう。