メイン

Transfer アーカイブ

2008年05月02日

Transfer

いよいよ、Transfer だ。
ちょうど、Transfer 1.0 RC がリリースされたようだ。
気になったのは、この2点。

 ・Composite Key Support
 ・Transaction Support

Composite Key ってなんぞや?
今まで、データベースの主キーとして、カラム一つしか指定できなかったっぽいのが、複数のカラムが指定できるようになったということなのだろうか?
どうやら、複数のカラムが指定できるようになったらしい。が、項目として指定できるのは、1つのみで、あとは、外部参照の項目を指定することのようだ。
基本的に正規化してテーブル設計すれば、問題はなさそうだけど、大丈夫なんだろうか。
古いシステムのテーブル設計によく見られる、4つとか、5つとかの項目からなる主キー設定で、なおかつ、
正規化してある訳じゃないテーブルには、使えないということか。

いよいよ、Transfer を読み解いていくことになるわけだが、まずは、データベースを作らなければいけない。
データベースは、環境に非常に影響を受けるので、著しく汎用性が落ちてしまうのが困りものだけど、仕方ない。

とりあえず、慣れている PostgreSQL で作成する。
バージョンは、8.1 だ。

table_01.gif

各テーブルの意味は、そのまんまだから、わかるだろう。
ちなみに、SERIAL 型というのは、データ挿入でオートインクリメントしてくれる INT 型。

データベース作成

createdb -E UNICODE SHOPPING

テーブル作成

CREATE TABLE public.CATEGORYMST (
categoryid INT NOT NULL
, categoryname TEXT NOT NULL
, PRIMARY KEY (categoryid)
);

CREATE TABLE public.ITEMMST (
itemid SERIAL NOT NULL
, lnkcategoryid INT NOT NULL
, itemname TEXT NOT NULL
, price INT NOT NULL
, PRIMARY KEY (itemid)
);

CREATE TABLE public.CARTDATA (
sessionid CHAR(32) NOT NULL
, lnkitemid INT NOT NULL
, categoryname TEXT NOT NULL
, itemname TEXT NOT NULL
, price INT NOT NULL
, PRIMARY KEY (sessionid, itemid)
);

データ登録

INSERT INTO CATEGORYMST (categoryid, categoryname) VALUES (1, 'マウス');
INSERT INTO CATEGORYMST (categoryid, categoryname) VALUES (2, 'キーボード');

INSERT INTO ITEMMST (itemid, lnkcategoryid, itemname, price) VALUES (1, 1, '光学式マウス', 2600);
INSERT INTO ITEMMST (itemid, lnkcategoryid, itemname, price) VALUES (2, 1, 'ボールマウス', 1100);
INSERT INTO ITEMMST (itemid, lnkcategoryid, itemname, price) VALUES (3, 1, 'レーザー式マウス', 4800);
INSERT INTO ITEMMST (itemid, categoryid, itemname, price) VALUES (4, 2, 'テンキー付きUSBキーボード', 1800);
INSERT INTO ITEMMST (itemid, lnkcategoryid, itemname, price) VALUES (5, 2, 'テンキーなしUSBキーボード', 1200);
INSERT INTO ITEMMST (itemid, lnkcategoryid, itemname, price) VALUES (6, 2, 'テンキー付きPS/2キーボード', 1100);
INSERT INTO ITEMMST (itemid, lnkcategoryid, itemname, price) VALUES (7, 2, 'テンキーなしPS/2キーボード', 900);
SELECT SETVAL('ITEMMST_itemid_seq', MAX(itemid)) FROM ITEMMST;

これで、データベースの準備はできた。次は、Transfer の設定だ。

2008年05月07日

Transfer の設定

例のごとく、今までのサンプルアプリケーションを変更していく。

まずは、config/transfer/Datasource.xml から。

<datasource>
 <name>shopping</name>
 <username></username>
 <password></password>
</datasource>

ColdFusion の管理画面で、データソースを設定して、その名前を入れるだけ。

次に、config/ColdSpring.xml 。今までコメントアウトしてきたところだ。

<alias alias="ormAdapter" name="ormAdapter.Transfer" />
<alias alias="ormService" name="ormService.Transfer" />
<bean id="transferConfiguration" class="transfer.com.config.Configuration">
 <constructor-arg name="datasourcePath"><value>/shopping/config/transfer/Datasource.xml</value></constructor-arg>
 <constructor-arg name="configPath"><value>/shopping/config/transfer/Transfer.xml</value></constructor-arg>
 <constructor-arg name="definitionPath"><value>/shopping/model/data/transfer</value></constructor-arg>
</bean>

この状態でアプリケーションが動くかどうか確認してみよう。
Model-Glue デバッグの2行目に、Using ORM Adapter: com.adobe.hs.common.orm.TransferAdapter とか表示されるだろうか。
なにかエラーのようなものがなければ、OKだ。

いよいよ、Transfer の肝、config/transfer/Transfer.xml 。

<objectDefinitions>
 <package name="master">
  <!-- Category -->
  <object name="Category" table="CATEGORYMST">
   <id name="PKeyCategoryId" type="numeric" column="categoryid" />
   <property name="CategoryName" type="string" column="categoryname" />
   <onetomany name="CategoryItemLink" lazy="true">
    <link to="master.Item" column="lnkcategoryid" />
    <collection type="struct">
     <key property="ItemId" />
    </collection>
   </onetomany>
  </object>
  <!-- Item -->
  <object name="Item" table="ITEMMST" sequence="ITEMMST_itemid_seq">
   <id name="PKeyItemId" type="numeric" column="itemid" generate="false" />
   <property name="ItemName" type="string" column="itemname" />
   <property name="Price" type="numeric" column="price" />
   <manytoone name="ItemCategoryLink" lazy="true">
    <link to="master.Category" column="lnkcategoryid" />
   </manytoone>
  </object>
 </package>
</objectDefinitions>

たぶん、いきなり見ても、わからないだろう。
package は、Transfer 上での意味しかなく、テーブルをグループ化するためのもの。
object は、テーブル。id は、主キーカラム。property は、カラム。
onetomany 、manytoone 、は、他のテーブルとのリレーション設定。
これだけ把握した上で、ざっと眺めれば、なんとかわかってくるだろうか。

2008年05月08日

Transfer の利用

では、Transfer を利用するように書き換えてみる。

ColdSpring.xml で設定していた、販売商品のマスタデータをデータベースから取得するようにするため、下記のように書き換え。

<bean id="itemArrayBean" class="shopping.model.ItemArrayBean">
 <property name="itemArray">
  <list>
   <map>
    <entry key="name"><value>光学式マウス</value></entry>
    <entry key="price"><value>2600</value></entry>
   </map>
   <map>
    <entry key="name"><value>ボール式マウス</value></entry>
    <entry key="price"><value>1100</value></entry>
   </map>
   <map>
    <entry key="name"><value>レーザー式マウス</value></entry>
    <entry key="price"><value>4800</value></entry>
   </map>
  </list>
 </property>
</bean>

 ↓

<bean id="itemArrayBean" class="shopping.model.ItemArrayBean" autowire="byType" />

マスタデータを設定する必要が無くなるため、ばっさり、削除。
その代わり、AutoWire で Bean を注入。

何を注入するのか。shopping.model.CartProcess に以下のコードを追加する。

<cffunction name="setOrmService" access="public" returntype="void" output="false">
 <cfargument name="ormService" type="transfer.TransferFactory" required="true">
 <cfset variables.transfer = arguments.ormService.getTransfer() />
</cffunction>

前回設定した ormService を渡して取得する Transfer オブジェクトを利用してデータベースからデータを取得するようにするのである。
この ormService は、type を見ればわかるように、transfer.TransferFactory である。

一方、ormAdapter の正体は、ModelGlue.unity.orm.transfer.TransferAdapter で、どうやら、Model-Glue が用意してくれたもののようだ。
いっそのこと、抽象クラスかインターフェースかなんかにキャストして、O-R マッピングで汎用的に使えるようになっていれば、おもしろそうだけど、俺が何か間違っているのだろうか。

shopping.model.ItemArrayBean で Transfer オブジェクトを受け取り、データを取得して、既存の戻り値と同じものを返せば、商品一覧をデータベースから取得して表示するだけなら、書き換えるプログラムは、この ItemArrayBean だけとなる。

2008年05月09日

Transfer がうまく動かない

Transfer の迷宮をさまよい中。
とりあえず、以前書いた

<objectDefinitions>
 <package name="master">
  <!-- Category -->
  <object name="Category" table="CATEGORYMST">
   <id name="CategoryId" type="numeric" column="categoryid" />
   <property name="CategoryName" type="string" column="categoryname" />
   <onetomany name="CategoryItemLink" lazy="true">
    <link to="master.Item" column="CategoryId" />
    <collection type="struct">
      <key property="ItemId" />
    </collection>
   </onetomany>
  </object>
  <!-- Item -->
  <object name="Item" table="ITEMMST" sequence="ITEMMST_itemid_seq">
   <id name="ItemId" type="numeric" column="itemid" generate="false" />
   <property name="ItemName" type="string" column="itemname" />
   <property name="Price" type="numeric" column="price" />
   <manytoone name="ItemCategoryLink" lazy="true">
    <link to="master.Category" column="categoryid" />
   </manytoone>
  </object>
 </package>
</objectDefinitions>

は、だめのようだ。

<objectDefinitions>
 <package name="master">
  <!-- Category -->
  <object name="Category" table="CATEGORYMST">
   <id name="CategoryId" type="numeric" column="categoryid" />
   <property name="CategoryName" type="string" column="categoryname" />
   <onetomany name="CategoryItemLink" lazy="true">
    <link to="master.Item" column="CategoryId" />
    <collection type="struct">
      <key property="ItemId" />
    </collection>
   </onetomany>
  </object>
  <!-- Item -->
  <object name="Item" table="ITEMMST" sequence="ITEMMST_itemid_seq">
   <id name="ItemId" type="numeric" column="itemid" generate="false" />
   <property name="ItemName" type="string" column="itemname" />
   <property name="Price" type="numeric" column="price" />
  </object>
 </package>
</objectDefinitions>

外部参照なわけで、相互に参照するような設定にしてはいけない。
相互に参照しないと、双方から参照できないと思っていたら、そんなことはないようだ。

カートのデータをデータベースに登録するプログラムに変更しようとして、面倒な問題に直面。
カートのデータを全消去というコードがなんとも、面倒だ。

もちろん、従来のO-R マッピングを利用しない書き方に比べて面倒ということではなく、折角の Transfer が意味のないコードになってしまうということだ。
やっぱり、Transfer を意識したテーブル設計をしなければならない。

テーブル CARTDATA をヘッダと明細に分けよう。
さて、引き続き、さまようか。

2008年05月12日

なんとか

なんとか、Transfer の迷宮を抜け出せつつある。もちろん、Transfer のほとんどの機能を把握したという意味ではない。

テーブルを下図のように変更。
table_02.gif

CREATE TABLE public.CARTHEADER (
sessionid VARCHAR(32) NOT NULL
, createdate TIMESTAMP NOT NULL
, updatedate TIMESTAMP NOT NULL
, PRIMARY KEY (sessionid)
);

CREATE TABLE public.CARTDATA (
lnksessionid VARCHAR(32) NOT NULL
, lnkitemid INT NOT NULL
, categoryname TEXT NOT NULL
, itemname TEXT NOT NULL
, price INT NOT NULL
, quantity INT NOT NULL
, PRIMARY KEY (lnksessionid, lnkitemid)
);

ついでに、カートデータとして、各商品の個数を登録できるようにした。

主キーに、固定長の文字列を設定して、バイト長が足りない文字列を入れることは、御法度らしい。
他にも、実際にやってみるとわかる Transfer のクセというか作法もちょこっとずつわかってきた。

サンプルアプリケーション

ColdFusion-Style フォーラムで話題があがっていたけど、アプリケーションフレームワークは色々あるが、DIコンポーネントは ColdSpring 、O-Rマッパーは、Transfer を選択する人が多いのだろうか。
アプリケーションフレームワークは ColdBox が流行なのかな。なにやら、Eclipse のプラグインがあるみたいだし、ちょっと読んでみる必要があるのかな。

目下の悩み事は、CFタグのコードと、cfscriptのコードが入り交じっていることだ。
全部CFタグにしたほうが、統一感があっていいのだけど、各種ドキュメントのサンプルコードが cfscript で書いてあったり、ちょっとだけコーディング量が少なかったりと、cfscript で書いちゃう場所もある。
どちらかに全て統一となると、cfscript に統一ということはできないようだから、やっぱり、CFタグの方がいいのかなぁ。

2008年05月13日

Transfer のトランザクション処理

サンプルアプリケーションは、データベースのトランザクション処理ができていない。
こんなアプリケーションが許されるわけはないので、トランザクションを実装しなければならない。
トランザクションはどのように実装するのか。

やり方としては、コンポーネント内の指定した関数を丸ごとトランザクション処理するというイメージになる。
shopping.model.CartProcess の transfer.TransferFactory を AutoWire したタイミングで、transfer.TransferFactory から transfer.com.sql.transaction.Transaction を取得する。

<cffunction name="setOrmService" access="public" returntype="void" output="false">
 <cfargument name="ormService" type="transfer.TransferFactory" required="true">
 <cfset variables.transfer = arguments.ormService.getTransfer() />
 <cfset variables.transaction = arguments.ormService.getTransaction() />
</cffunction>

その Transaction を利用して、

 <cfset transaction.execute(component, methodName, [arguments]) />

と、直接的に関数をトランザクション指定で起動することもできる。
直接指定の場合は、その関数を利用するときに、上記の記述で利用することになる。

また、AOPのように記述して、関数を指定することで、指定した関数が丸ごとトランザクション処理されるようだ。
この記述の場合は、事前に行っておくことで、その関数を通常のように利用した場合でも、トランザクション処理されることになる。

<cffunction name="setOrmService" access="public" returntype="void" output="false">
 <cfargument name="ormService" type="transfer.TransferFactory" required="true">
 <cfset variables.transfer = arguments.ormService.getTransfer() />
 <cfset variables.transaction = arguments.ormService.getTransaction() />
 <cfset variables.transaction.advise(this, insertItem) />
</cffunction>

さらに、

 <cfset arguments.transaction.advise(this, "^save") />

と、正規表現で一括してトランザクション処理指定もできる。
トランザクションで処理したい関数を save で始まる関数名にしておけば、簡単に一括指定できる。

2008年05月23日

Scaffold のカスタマイズ

Scaffold には、用意されたデザインではなく、自由にデザインを変更できる機能がある。
これを使えば、もしかすると、本番システムにおいても利用できる機能なのかもしれない。

続きを読む "Scaffold のカスタマイズ" »

2008年05月28日

TransferDecorator

Transfer の機能にある、decorator とはなんだろうか。
どこかで聞いたような気がしたんだけど、オブジェクト指向に出てくるデコレータパターンかな。
オブジェクトの振る舞いを自由に変更できるということか。

例のごとく、ドキュメントを読んでも、さっぱりわからない。
悪戦苦闘の結果に導き出した答えが、「Transfer のオブジェクトの振る舞いを変更するための仕組み」。
Transfer のオブジェクトは、Transfer.xml の object タグの情報を元に、自動で生成される。Transfer.xml で設定できること以外は、そのオブジェクトに対しては、ブラックボックス状態だ。
そこで、この transfer.com.TransferDecorator の出番となる。

続きを読む "TransferDecorator" »

2008年05月29日

Transfer の clone 機能

Transfer は、clone 関数が用意されている。文字通り、オブジェクトの複製ができる。
get で取得した Transfer のオブジェクトを clone するだけだ。

 user = getTransfer().get("user.User", 1);
 cloneUser = user.clone();

どんなときに使うのだろうか。

続きを読む "Transfer の clone 機能" »

2008年06月02日

Transfer Event Model

Transfer Event Model とは、なんだろうか。
データベースのテーブルをマッピングした Transfer オブジェクト経由でデータベース上のデータに対してなんらかの変更が加えられた(もしくは、変更が加えられる)ことを監視して、処理を発生させることができるということのようだ。

続きを読む "Transfer Event Model" »

2008年06月03日

Observer を Inject する

Google Groups に参考になるスレッドが上がってた。
addXXXObserver(component), where to call? is singleton required?
そこからのリンクMy Take on Transfer ORM Event Model - BeforeCreate Example
Observer の登録の仕方だ。これを参考にすると、すっきりした。

続きを読む "Observer を Inject する" »

2008年06月04日

ちょっと楽になるかな?ツール

もともと、無精なものだから、どうにも Transfer の Generated Methods の入力が煩わしい。
多少でも楽になればと思い、ちょっとツールを作ってみた。
これを使えば、
developing_04.gif
こういう風に、一覧で利用できる関数を表示してくれる。

続きを読む "ちょっと楽になるかな?ツール" »

2008年06月05日

ManyToMany

Transfer のリレーション設定の一つである manytomany って、なんとなく見過ごしてきたけど、実際のところ、これって何よ?というわけで、調べてみた。
多対多ということなんだろうけど、浅学なのか、こういうリレーションてのは、あまり考えたことがなかった。

例えば、ユーザーというデータがあって、そのユーザーが購入した商品は、複数ある。
逆に、ある商品を購入したユーザーは、複数人いるというのが、多対多ということらしいけど、どうやって設定するのやら。
以前、Transfer のサンプルの tBlog の Transfer.xml で manytomany を見かけたけことを思い出して、参考にしてみた。
インターネット上の情報から考えても、どうやら、リレーション用のテーブルを設けるらしい。

続きを読む "ManyToMany" »

2008年06月06日

ColdSpring の Remote Facades

ColdSpring のドキュメントに Remote Facades という項目がある。
これも確か、デザインパターンで見たような気がする。
便利なサイトがあったので、参考にさせてもらった。ありがたい。
なるほどなるほど。
実際の実装にはここを参考にさせてもらった。

で、とりあえず、http://localhost/shopping/model/ItemArrayBean.cfc?method=getItemArray にアクセス。

コンポーネント [[リポジトリ]]\shopping\www\model\ItemArrayBean.cfc のメソッド 'getItemArray' はリモートでアクセスできません。

cffunction の access 属性が public なのだから当然。

続きを読む "ColdSpring の Remote Facades" »

2008年06月09日

Transfer のドキュメント生成ツール

以前のエントリで、Generated Method を一覧してくれるツールをアップしたけど、やっぱり、公式なツールとしてすでに存在していたようだ。

こんな感じで関数を一覧してくれるので、紙のドキュメントにすることも可能だ。

続きを読む "Transfer のドキュメント生成ツール" »

2008年09月11日

Transfer でのエラー発生時の愚痴

Transfer を使っていると、何か問題があったときに、どこがいけないのか、本当にわかりにくい。
たいていの場合は、Transfer.xml の書き方が間違っている場合が多いのだけど、実際にどんなクエリを発行しようとしてエラーになったのか、どんなクエリを発行したのか。

続きを読む "Transfer でのエラー発生時の愚痴" »

2008年09月22日

Railo 浮上

以前のエントリーで、Railo に見事撃沈された我がサンプルアプリケーション。
その Railo が正式リリースされたと聞いたので、もう一度、動作確認してみた。

続きを読む "Railo 浮上" »

2008年11月05日

CF Frameworks Explorer ってなんぞ?

CFEclipse を導入する際に、ずっと気になっていたけど、

cfframeworks01.gif

こんな風だし、

cfframeworks02.gif

右クリックで、“構成”を選択すると、

cfframeworks03.gif

こんな警告出るし、

こんなウィンドウが表示されるだけで、なんなんだよ、これ。って感じで、放置していた CF Frameworks Explorer 。

でも、ここ の「08 CF Frameworks Explorer Introduction」を見て、一気に解決。

続きを読む "CF Frameworks Explorer ってなんぞ?" »

2008年11月10日

Transfer で発行されるクエリ

Transfer では、データベースクエリを発行する場所は、transfer.com.sql.QueryExecution の executeQuery() に集約されているので、どんなクエリが発行されているのかを把握することが簡単である。

実際に、80行目に以下のコードを入れてみた。

続きを読む "Transfer で発行されるクエリ" »

About Transfer

ブログ「気楽に行こう」のカテゴリ「Transfer」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

前のカテゴリはModel-Glueです。

次のカテゴリはWindowsCEです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。