2005/03/30EDbUtilsは、EStoreのDB編このレポートは、ショッピングカートの実装を通じてオープンソースツールをどのように活用するかを検証するレポートのDB編である。ツール使いこなすUNIX時代には「いかにしてツールを使うか」がプログラミングセンスの見せ所であった。言語もCからjavaに移って、開発環境もGUIペースのEclipseに取って替わられた感がある今日この頃だが、このような状況でどのようにしてソフトウェアを開発すればよいのか、オープンソースのツールを使いながら紹介する。DbUtilsを使いこなすJDBCプログラミングにすぐ効くDbUtils活用マニュアルに紹介されたDbUtilsは、検索後のデータマッピングに優れており、サイズも小さいので小さいシステムでのDB構築に向いていると思われる。ERマッピングツール(1)と比べるとデータの挿入や更新処理に対して個別にAPIを使ったコーディングが必要である。EDbHelperとEDBUtilsというユーティリティクラスを使って、DbUtilsをもう少し使いやすくしてみる。(2)私は、OJBしか使ったことがないEDbhelperのjarファイルダウンロードとAIPを参照されたい。使い込み必要な改良を加えてから、ソースも公開する予定である。EDbUtilsを使ってみる最初に単純な例でEDbUtilsがどれくらい簡単にDBにアクセスできるかを例を使って説明する。setterの生成を行った。address)のコンストラクタも用意した。サンプルプログラムSample1.javaのソースは、以下の通りである。EDbUtils.dropTable(Member.class);}}動作試験の前に、使用するデータベースHSQLDBをサーバモードで起動する必要がある。1.7.1のdemoにあるrunServer.batを使ってDBサーバを起動する。runServer.bat次にサンプルプログラムを実行するが、EDbUtilsは以下のライブラリを使用するためcommons-dbcp.jarcommons-dbutils-1.0.jarcommons-logging.jarcommons-pool.jarlog4j-1.2.5.jarhsqldb.jar最後のhsqldb.jarは、使用するデータベースによって変更する必要がある。Sample1を実行すると以下の結果がコンソールに出力される。1.7.1のdemoにあるrunManager.batを使って調べることができる。Sample1.javaの処理をブロック毎に説明する。前処理のブロックでは、objsには、DBに登録するオブジェクトをセットするEDbUtils.createDatasource(ドライバ名,DBのURL,ユーザ名,パスワード)を指定してDataSourceを作成するEDbUtils.createHelperMap(テーブルに登録するオブジェクトの配列)を渡して、EDbHelperのマップを作成するEDbUtils.createTablesでDBにテーブルを作成する(4)の処理を行う。"東京都中野区");EDbUtils.insertObject(user1);DBからオブジェクトを取り出すには、idに対応するオブジェクト(5)を読み込むEDbUtils.loadObjectと複数のオブジェクトを取り出すEDbUtils.selectObjectsがある。EDbUtils.loadObjectの処理は、次のようになる。user1.getId());System.out.println(EDbUtils.toString(user2));同様にEDbUtils.selectObjectsは、次のようになる。SQL文のWHERE文)でオブジェクトを選択する。WHERE文に""を指定するとすべてのレコードが返される。(Member)list.get(i);System.out.println(EDbUtils.toString(member));}スタンダードモードでも問題なく動作するが、デバッガで処理の途中のチェックをするためにサーバモードで起動している。トランザクションの処理のチェックには、有効である。EDbHelperの処理EDbUtilsでは、テーブルの作成、レコードの挿入、レコードの更新、レコードの削除等DbUtilsに含まれていない機能をヘルパークラスEDbHelperを内部で使って処理している。EDbHelperは、javaのリフレクションの機能を使ってBeanの属性とその型に応じたSQL文を生成する。Sample1で使ったMemberクラスを例にどのようなSQL文が生成されるか示す。EDbHelper(member);System.out.println(helper.getCreateTableStmt());System.out.println(helper.getInsertStmt(member));System.out.println(helper.getUpdateStmt(member));System.out.println(helper.getDeleteStmt(member));以下のような出力を得る。TAKEMOTO'をリフレクション機能でオブジェクトmemberから値を取得して挿入文を作成するUPDATE文は、オブジェクトmemberのIDを条件に更新するレコードを指定して、オブジェクトの値をリフレクションを使ってVALUES項にセットして更新文を作成するDELETE文は、オブジェクトmemberのIDを条件に削除するレコードを指定して削除文を生成するのように各SQL文が組み立てられる。(6)レコードの識別子名をIDに統一することによってEDbHelperの構造も簡単になっている。オブジェクト間の関連オブジェクト間の関係をDBのテーブルにマッピングする場合を例に取ってみる。例として、本(Book)とその著者(People)の関係を1:1対応(7)について考えてみる。クラスの関係は、のようにBookから著者authorを参照している。DBでは、参照するauthorオブジェクトの識別子をauhtorRefで関係付けするものとする。そのような場合、データのロードや挿入時にこれらの関係を処理する必要が出てくる。この処理をするインタフェースがIEPropertiesである。IEPropertiesインタフェースでは、データの挿入時に関係する属性を処理するinsertPropertiesデータのロード時に関係する属性をセットするloadProperties関連する属性の表示を行うprintPropertiesを実装する必要がある。insertPropertiesの例BookのinsertPropertiesは、つぎのようにものになる。author.getId();}}著者への参照オブジェクトauthorがNULLで無かったら、authorをDBに挿入し、その識別子をauthorRefにセットする。loadPropertiesの例insertPropertiesとは、逆にloadPropertiesでは、Peopleテーブルへの参照IDであるauthorRefがNULLでない場合に、Peopleテーブルから著者authoreをloadObjectを使ってロードし、セットする。printPropertiesの例printPropertiesは、DBとは直接関係はないが、デバッグ時にオブジェクトの内容を出力するときに使用する。不要の場合には、空のメソッドを定義すればよい。Bookでは、"author->(著者オブジェクトの内容)"のような形式で出力している。PeopleのサンプルプログラムSample2.javaを示す。Bookのインスタンをテーブルに挿入する例EDbUtils.insertObject(book);の部分でbookオブジェクトをDBに挿入するとその参照オブジェクトであるauthorも同時にDBに挿入されている点に注意されたい。線形リストの例もう一つ線形リストにIEPropertiesを使った例を示す。nextRefが属性として定義され、Bookと同様にIEPropertiesのメソッドを実装したのが、LinkedList.javaである。l1.getId())で線形リストの構造を完全に復元することができる。Sample3の実行結果は、次のようになる。next->NULL))すこし見にくいがl1->l2->l3のリンクが正常に復元されているのが分かる。Sample3のソースは、以下の通りである。EDbUtilsの制約これまでの説明で、EDbUtilsを使うことによってオブジェクトとDBの連携がきわめてスムーズに行われているような印象を得るだろう。これにはいくつかのトリックがある。オブジェクトは、必ずidという属性を持つ必要がある。DBのテーブルでは、IDを主キーとするテーブルが作成される主キーの値は、HSQLDBのIDENTITYを使っており、使用するDBに依存する。TABLE文を手で修正する必要がある。オブジェクトは、引数なしのコンストラクタを持つ必要がある。EDbHelperで扱えるオブジェクトに、内部クラスは使用できない。TIMESTAMPが使用できる。EDbHelperは、コメントを入れても1000行に満たない小さなユーティリティであるが、いくつかの規約と制限を享受することによって、DbUtilsというツールを有効に利用することが可能になる。説明が抜けていたが、volatile宣言された属性は、テーブルのフィールドへのマッピングは行われないので、この機能を利用すれば、不要なフィールドのDBへの投入を防ぐことができる。オブジェクトの更新と削除オブジェクトの更新と削除について、EDbUtilsのjunit用単体テストコードを例に説明する。オブジェクトの更新オブジェクトの更新は、きわめて簡単で変更したオブジェクトに対して、EDbUtils.updateObject(変更したオブジェクト)を実行するだけである。AAA"に更新され、EDbUtils.loadObjectでロードされたretと等しいことが確認できる。オブジェクトの削除オブジェクトの削除は、もっと簡単である。EDbUtils.deleteObjectに削除したいオブジェクトを渡すだけである。トランザクション処理DbUtilsのドキュメントには、コネクションを接続してからの処理に対し、トランザクション処理を行うことができる旨の説明がある。これには、データベースのAUTOCOMMITをFALSEにする必要がある。deleteObjectにコネクションとヘルパーマップを先頭に追加した引数を持つメソッドを提供し、トランザクション処理を実現できるようにしている。junitの単体テストメソッドtestTransactionを使ってトランザクション処理を説明する。ツリー構造のサポートコンポジットパターンのようなツリー構造をデータベースにマッピングするためにEDbUtilsを拡張し、以下のようなノードマップテーブルを導入し、複数のクラスのインスタンスからなるリストを扱えるユーティリティメソッドを追加した。追加したメソッドは以下の通りである。オブジェクトをENODEMAPPERにマップし、DBに挿入する現在のところ、正式サポートではなくあくまでも実験実装である。詳しくは、AIPを参照されたい。relaxerを使ったメニューツリーrelaxerは、RELAXで記述されたXMLをjavaのクラスにマッピングするツールであり、XMLとjavaのオブジェクトマッピングの他に、コンポジットパターン、ビジターパターンを扱う構造への変換をサポートしている。Itemからそのサブクラスexmpale.relaxer.emenuに変換し、XMLファイルのツリー構造をデータベースに挿入とデータベースからの読み込みを行う例を示す。example.relaxer.emenuのクラス関連図を以下に示す。Menubarは、複数のMenuから構成され、Menuには、MenuまたはItemが含まれるツリー構造を持っている。relaxerのRELAXソースmenu.rlxは、参考文献のメニューサンプルから引用した。</elementRule></module>RELAXソースからjavaのソースに変換するrelaxerの起動は、以下の通りです。ビジターの処理relaxerの生成したソースを加工してDBへのアクセス処理を追加することも可能であるが、それでは仕様変更に弱い実装になる。EItemのツリー構造に変換する。変換するビジターEMenuVisitorのソースを以下に示す。menubar;}}ビジターは、relaxerが生成したRVisitorBase.javaをコピー&ペーストして作成した。ビジターの処理は、きわめて単純であり、enterメソッドのvisitable毎に対応するexample.relaxer.emenuパッケージのオブジェクトを生成するオブジェクトのコンポジットをクリアするスタックから取り出した親ノードに生成したオブジェクトを追加する(12)オブジェクトをスタックにプッシュする各leaveメソッドにスタックのポップ処理を挿入するを実装すればよい。いずれかの場合があるため、instaceofを使って処理を振り分ける。IMenuChoiceに子ノードの追加機能があるといいのかも知れません。順序は逆になったが、パッケージexample.relaxer.emenuのクラスを実装する。setterを生成するIEPropertiesのメソッド、insertProperties、loadProperties、printPropertiesを実装するだけである。EMenu.javaを例にinsertPropertiesの処理を見る。基本的には、コンポジットがあれば、getNewOwnerIdメソッドで新しい所有者IDを取得し、それを元にinsertMappedListメソッドを呼び出すだけである。Arrays.asList(getContent()));}同様にloadPropertiesは、contentOwnerIdがnullで無い場合、loadMappedListメソッドでコンポジットのリストを取得し、コンポジットに追加するだけである。EItemの完全なソースを以下に示す。integer;}}親ノードへの逆参照は、再帰的になるため、残念ならEDbUtilsでは処理できなかった。テストプログラム最後に、XMLからメニュー情報を読み込みDBに投入するテストプログラムを作成する。Sample4.javaがテストプログラムである。Menubarのuriに入力ファイルを指定し、Menubarを生成する。e){System.err.println(e.toString());}}}テストデータを以下に示す。ピアソン・エデュケーション,。