3. コンピュータ商品の登録
プロパティにリスト項目を含むエンティティの登録
それでは、ここから実際のサンプルを説明します。
コンピュータ商品を購入する場合、例えば、デスクトップ・コンピュータでは、CPUの種類やメモリー容量、OSなどを選択できるようにするのが普通です。そして、このような選択項目を登録する場合には、プロパティ項目をリスト形式にできるBigtableのキー・バリュー・ストアは、大変都合のよいデータ構造といえます。このデータ構造をRDBで実現しようとすると、正規化によるテーブル細分化がかなりの数になり、データベース管理の煩雑化が避けられません。
3.1. コンピュータ商品マスター登録画面

|
図6: デスクトップPCの商品登録 |
図6は、デスクトップPCの登録画面です。今回の画面では、CPU、メモリー、OSの登録フィールドにそれぞれカンマ区切りで複数入力しています。例えば、CPUでは「Core i5」「Core i7」「Core2 Quad」が入力されています。データを入力後、「登録」ボタンのクリックによって、データはサーバー側のビーンズに送られ、複数入力項目は、リスト形式のプロパティ・データとしてBigtableに登録(永続化)されます。

|
図7: 液晶ディスプレイの商品登録 |

|
図8: プリンタの登録 |
デスクトップPCのほか、図7では液晶ディスプレイ、図8ではプリンタを登録しています。基本項目以外の場合と同様に、複数のスペック項目入力ができるようになっています。ただし、プリンタの解像度では、複数入力が想定されているフィールドに、わざと1つの項目だけを入力して登録しています。
[Datastore Viewer での登録(永続化)データ確認]
次に、図6から図8の画面で登録されたデータを、App EngineのDatastore Viewerで確認してみます。

|
図9: Datastore Viewer での登録データ確認(クリックで拡大) |
図9は、Datastore Viewerで確認できる、登録(永続化)されたデータの内容です。図中でcpuやmemoryなどの複数項目をカンマ区切りで登録したフィールドは、すべてリスト・データとして格納されていることが分かります。また、複数項目入力が想定されているフィールドでは、1項目だけ登録された「resolution」プロパティの場合も、リスト形式でプロパティ・データが登録されています。
3.2. 商品マスター登録サーバー側プログラム
ここからは、登録処理のプログラムを説明します。前回同様、DWRを使用しているため、サーバーで記述する必要があるのはビーンズ・メソッドだけです(図10のEclipseプロジェクト構成を参照)。

|
図10: Eclipseプロジェクトの構成 |
【リスト1】: 商品マスター登録サーバー側プログラム(addItemメソッド)
02 | import java.util.Hashtable; |
03 | import com.google.appengine.api.datastore.DatastoreService; |
04 | import com.google.appengine.api.datastore.DatastoreServiceFactory; |
05 | import com.google.appengine.api.datastore.Entity; |
06 | import com.google.appengine.api.datastore.Key; |
07 | import com.google.appengine.api.datastore.KeyFactory; |
09 | import com.google.appengine.api.datastore.FetchOptions; |
10 | import com.google.appengine.api.datastore.Query; |
11 | import java.util.Arrays; |
14 | DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); |
16 | public String addItem(Hashtable query){ |
17 | String itemNo =(String)query.get( "itemNo" ); |
18 | Key itemNoKey = KeyFactory.createKey( "itemMas2" , itemNo); |
19 | Entity imas = new Entity(itemNoKey); |
20 | imas.setProperty( "type" , (String)query.get( "type" )); |
21 | imas.setProperty( "itemName" , (String)query.get( "itemName" )); |
22 | imas.setProperty( "price" , (String)query.get( "price" )); |
23 | String kind =(String)query.get( "kind" ); |
24 | if (kind.equals( "desktop" )){ |
26 | imas.setProperty( "cpu" , |
27 | Arrays.asList(((String)query.get( "cpu" )).split( "," ))); |
28 | imas.setProperty( "memory" , |
29 | Arrays.asList(((String)query.get( "memory" )).split( "," ))); |
30 | imas.setProperty( "os" , |
31 | Arrays.asList(((String)query.get( "os" )).split( "," ))); |
32 | } else if (kind.equals( "display" )){ |
34 | imas.setProperty( "size" , |
35 | Arrays.asList(((String)query.get( "size" )).split( "," ))); |
36 | } else if (kind.equals( "printer" )){ |
38 | imas.setProperty( "papersize" , |
39 | Arrays.asList(((String)query.get( "papersize" )).split( "," ))); |
40 | imas.setProperty( "resolution" , |
41 | Arrays.asList(((String)query.get( "resolution" )).split( "," ))); |
45 | return "登録成功: itemNO=" +itemNo; |
リスト1に示した全体のプログラム構造は、前回とほとんど変わりません。リスト・プロパティの永続化データは、デスクトップPC、液晶ディスプレイ、プリンタに対応して、それぞれ(1)~(3)で行われています。
例えば、(1)最初の項目では、CPU種類の選択項目を永続化します。この部分のコードを確認してみます。
setPropertyの第2引数は、Arrays.asList(((String)query.get("cpu")).split(",")));となっています。
- 1. (String)query.get("cpu"))
- クライアントから送信されてきたクエリー項目「cpu」に対応するデータを取得し、文字列として返します。
- 2. ((String)query.get("cpu")).split(",")
- 1. で所得したcpu対応データは、カンマ区切りになっています。これに.split(",")を適用して文字列分割を行い、配列として返します。
- 3. Arrays.asList(((String)query.get("cpu")).split(",")));
- 2. で生成した配列をArrayクラスのasListメソッドでリスト形式に変換します。
このようにして生成されたリストをEntityクラスのインスタンス「imas」のsetPropertyの第2引数にセットすることによって、永続化プロパティとしての指定が終わります。
これ以外の部分は、第2回のサンプル・コードと同じです。リスト・プロパティの永続化(書き込み)も、Low-Level APIを使用すれば簡単に行えます
3.3. 商品マスター登録クライアント側プログラム
【リスト2】: 商品マスター登録クライアント側プログラム(addItem.htm)
005 | <title>コンピュータ商品マスタ登録</title> |
006 | <link type= "text/css" rel= "stylesheet" href= "/csslib/style.css" /> |
007 | <script type= "text/javascript" src= "/dwr/interface/colBean.js" ></script> |
008 | <script type= "text/javascript" src= "/dwr/engine.js" ></script> |
009 | <script type= "text/javascript" src= "/dwr/util.js" ></script> |
010 | <script type= "text/javascript" src= "/jslib/jquery-1.4.2.min.js" ></script> |
011 | <script type= "text/javascript" > |
013 | window.resizeTo(410, 530); |
014 | window.moveTo(10, 10); |
016 | var spectag1 = '<table width="380" border="1" bgcolor="#eeeeee">' |
017 | + '<tr><th width="90">CPU</th>' |
018 | + '<td><input type="text" size="40" id="cpu"/></td></tr>' |
019 | + '<tr><th>メモリ</th><td><input type="text" size="40" id="memory"/></td></tr>' |
020 | + '<tr><th>OS</th><td><input type="text" size="40" id="os"/></td></tr>' |
021 | + '<tr><th>ステータス</th><td id="status"></td></tr></table>' ; |
022 | var spectag2 = '<table width="380" border="1" bgcolor="#eeeeee">' |
023 | + '<tr><th width="90">サイズ</th>' |
024 | + '<td><input type="text" size="40" id="size"/></td></tr>' |
025 | + '<tr><th>ステータス</th><td id="status"></td></tr></table>' ; |
026 | var spectag3 = '<table width="380" border="1" bgcolor="#eeeeee">' |
027 | + '<tr><th width="90">用紙サイズ</th>' |
028 | + '<td><input type="text" size="40" id="papersize"/></td></tr>' |
030 | + '<td><input type="text" size="40" id="resolution"/></td></tr>' |
031 | + '<tr><th>ステータス</th><td id="status"></td></tr></table>' ; |
032 | var spectag4 = '<table width="380" border="1" bgcolor="#eeeeee">' |
033 | + '<tr><th colspan="2">選択エラー</th></tr>' |
037 | $( "#type" ).val( "デスクトップPC" ); |
038 | $( "#spec" ).html(spectag1); |
039 | $( "#kind" ).click( function (e){ |
040 | var stitle = "" , spectag = "" ; |
044 | $( "#spec" ).html(spectag1); |
045 | } else if (kind== "display" ){ |
047 | $( "#spec" ).html(spectag2); |
048 | } else if (kind== "printer" ){ |
050 | $( "#spec" ).html(spectag3); |
052 | $( "#spec" ).html(spectag4); |
054 | $( "#stitle" ).text(stitle); |
055 | $( "#type" ).val(stitle); |
058 | $( "#add" ).click( function (e){ |
060 | query[ "itemNo" ]=$( "#itemNo" ).val(); |
061 | query[ "type" ]=$( "#type" ).val(); |
062 | query[ "itemName" ]=$( "#itemName" ).val(); |
063 | query[ "price" ]=$( "#price" ).val(); |
064 | if (kind == "desktop" ) { |
065 | query[ "cpu" ] = $( "#cpu" ).val(); |
066 | query[ "memory" ] = $( "#memory" ).val(); |
067 | query[ "os" ] = $( "#os" ).val(); |
068 | } else if (kind == "display" ) { |
069 | query[ "size" ] = $( "#size" ).val() |
070 | } else if (kind = "printer" ){ |
071 | query[ "papersize" ] = $( "#papersize" ).val(); |
072 | query[ "resolution" ] = $( "#resolution" ).val(); |
074 | colBean.addItem(query, function (stat){ |
075 | $( "#status" ).text(stat); |
081 | $.each([ "itemNo" , "itemName" , "price" ], function (){ |
082 | $( "#" + this ).val( "" ); |
084 | $( "#itemNo" ).focus(); |
089 | <body bgcolor= "#cccccc" > |
090 | <h2 style= "color: #aa0022" >コンピュータ商品登録 |
091 | <input type= "button" id= "add" value= " 登録 " /> |
094 | <input type= "radio" name= "pc" id= "desktop" checked= "checked" />デスクトップPC |
095 | <input type= "radio" name= "pc" id= "display" />液晶ディスプレイ |
096 | <input type= "radio" name= "pc" id= "printer" />プリンタ |
098 | <table width= "380" border= "1" bgcolor= "#eeeeee" > |
099 | <tr><th id= "stitle" colspan= "2" >デスクトップPC</th></tr> |
101 | <th width= "90" >製品番号</th> |
102 | <td><input type= "text" size= "12" id= "itemNo" /></td> |
104 | <tr><th>タイプ</th><td><input type= "text" id= "type" /></td></tr> |
105 | <tr><th>製品名</th><td><input type= "text" size= "28" id= "itemName" /></td></tr> |
106 | <tr><th>価格</th><td><input type= "text" size= "10" id= "price" /></td></tr> |
リスト2は、商品マスター登録のクライアント側プログラムです。登録処理では、図6から図8のように、基本項目のほかにスペック項目をカンマ区切りで入力した後、「登録」ボタンをクリックします。クリックにより(1)の匿名関数が呼び出されて、サーバー送信データの連想配列セットが行われます。
例えば、デスクトップPCでは、(2)でスペック項目のセットが行われています。カンマ区切りの文字列をそのまま連想配列にセットするだけで、プログラム処理は前回と全く変わりません。つまり、リスト・プロパティ登録は、クライアントのプログラムは変更なしで済ませることもできるということです。最後に、(3)でサーバーへ登録リクエストを送信します。この部分も、前回と同様に、DWRのクラス・メソッド書式を使用したシンプルな書式になっています。