DATE : 2006/05/26 (Fri)
(前回の記事)
コンポーネント ID の表現方法
コンポーネント ID を特定する方法として、相対表現と絶対表現があります。
- コンポーネント ID の相対表現
特定のコンポーネントを中心とした表現方法です。
例えば、次の例で、「form1」の f:form から「table」の h:dataTable を指定するとします。
... <f:view> <f:form id="form1"> <h:dataTable id="table" ...> ... </h:dataTable> </f:form> </f:view> ...
相対表現では、「form1:table」と表現します。
- コンポーネント ID の絶対表現
コンポーネントツリーのルートにあるコンポーネントを中心とした表現方法です。JSF では、f:view 要素がコンポーネントツリーのルートにあたります。
絶対表現を行うには、「:」を表現の先頭に付けます。
つまり、上の例の絶対表現は「:form1:table」となります。
ここで、前回の最後に示した例に戻ります。
... <f:view> <f:form target="_blank"> <h:dataTable id="items" ...> ... </h:dataTable> </f:form> <f:form> <t:dataScroller for="items" ...> ... </t:dataScroller> </f:form> </f:view> ...
t:dataScroller 要素の for 属性「items」は相対表現です。そのため、上の例では2つ目の f:form 要素内でコンポーネント ID が items の要素を探していることになります。
そこで、絶対表現に書き換えます。ファイルパスであれば、親ディレクトリを示す表記方法がありますが、コンポーネント ID にはそのような方法はないようです(調査不足かもしれません)。
まず、コンポーネント ID を表記しやすいように、1つめの f:form 要素に id 属性を付けます。
... <f:view> <f:form id="table_form" target="_blank"> <h:dataTable id="items" ...> ... </h:dataTable> </f:form> <f:form> <t:dataScroller for="items" ...> ... </t:dataScroller> </f:form> </f:view> ...
「table_form」という ID を付けました。
そして、 h:dataTable のコンポーネント ID を絶対表現で表します。絶対表現は、「:table_form:items」となります。
この絶対表現を t:dataScroller の for 属性にすれば、 t:dataScroller が h:dataTable を参照できるようになります。
... <f:view> <f:form id="table_form" target="_blank"> <h:dataTable id="items" ...> ... </h:dataTable> </f:form> <f:form> <t:dataScroller for=":table_form:items" ...> ... </t:dataScroller> </f:form> </f:view> ...(了)
DATE : 2006/05/24 (Wed)
コンポーネント ID
DataScroller では、ページ単位で表示を行う一覧(DataTableなどのjavax.faces.component.UIData)を指定するために、そのコンポーネントの ID を t:dataScroller の for 属性に設定します。
例えば、「items」というコンポーネント ID の DataTable を DataScroller に指定するときは、次のようなコードになります。
(「...」の部分は省略部分です) ... <f:view> <f:form> <h:dataTable id="items" ...> ... </h:dataTable> <t:dataScroller for="items" ...> ... </t:dataScroller> </f:form> </f:view> ...
上の例では、1つの f:form 要素の中に h:dataTable 要素と t:dataScroller 要素があります。
しかし、 h:dataTable と t:dataScroller とで別々の f:form 要素が必要な場合はどうなるでしょうか。例えば、 h:dataTable では表内のボタンを押すと別ウィンドウで詳細な情報を表示し、 t:dataScroller のボタンを押すと同じウィンドウでページの切り替えを行うという場合です。素直に上記の例を書き換えると、次のようになります。
... <f:view> <f:form target="_blank"> <h:dataTable id="items" ...> ... </h:dataTable> </f:form> <f:form> <t:dataScroller for="items" ...> ... </t:dataScroller> </f:form> </f:view> ...
ところが、この例はうまく動作しません。 t:dataScroller 要素の for 属性である items が見つからないというエラーが出ます。
この理由は、コンポーネント ID は同じ階層で一意であれば良いという条件に基づきます。初めの例の場合、h:dataTable と t:dataScroller は同じ f:form 要素の直下にあるので同じ階層にあります。しかし、次の例では h:dataTable と t:dataScroller は別々の f:form 要素の直下にあるため、違う階層にあります。
同じ階層であればコンポーネント ID が一意ということは、逆に言えば階層が違えばコンポーネント ID は重複しても良いということになります。
これが、先程のエラーの原因です。for 属性で item という ID 持つ h:dataTable を指定したものの、t:dataScroller よりも上の階層や別の階層では ID が重複している可能性があるため、他の階層にある h:dataTable を特定できない仕様になっているのです。
そこで、ID の指定の仕方を少し変える必要がでてきます。
(つづきます)
DATE : 2006/05/22 (Mon)
Entity クラスの条件
Java Persistence API では、永続化の対象とするクラスのことを Entity クラスと呼びます。
Entity クラスを定義するためには、以下の条件を満たさなければなりません。
- Entity アノテーションを付加するか、XML ファイルで Entity として設定する。
- 引数なしのコンストラクタを用意する(アクセス修飾子は public か protected)。
- トップレベルのクラスであること(クラスやインタフェースの中で宣言されたクラスではないこと)。
- final クラスではないこと。
- RMI などで オブジェクトを直列化しなければならないときは、java.io.Serializable を実装すること。
- 他のクラスからインスタンス変数にアクセスさせる際は、アクセッサメソッド(getter/setter)を通して行う(インスタンス変数のアクセス修飾子は、public 以外にする)。
- プライマリーキーを表すプロパティを用意し、Id アノテーションを付加すること。
DATE : 2006/05/17 (Wed)
Java Persistence API とは
Java Persistence API とは、リレーショナルデータベースを使用してオブジェクトを永続化する API です。要するに、Java のオブジェクトをリレーショナルデータベースに保存するための API と考えれば簡単です。
Java Persistence API は、本来は EJB 3.0 に含まれる予定でしたが、現在では EJB 3.0 とは独立して提供されています。そのため、Java SE 環境下でも使用することができます。
今回の記事では、Java Persistence API の中でも、O/R マッピングの部分について簡単にメモする予定です。
O/R マッピングとは、Java のオブジェクトとリレーショナルデータベースとを関係付けることを言います。リレーショナルデータベースでは、データは表として表現されるため、オブジェクトをそのまま格納できない場合も出てきます。そこで O/R マッピングを行うことで、オブジェクトをリレーショナルデータベースに格納できる形に変換するわけです。
そのため、クラスに対して多少の設定が必要となってきます。
(つづきます)
DATE : 2006/05/16 (Tue)
Java には、System#arraycopy(Object, int, Object, int, int) という、配列の中身を高速にコピーするメソッドが用意されています。
このメソッドは Java ではなく機械語で実装されています。そのため、 for ループなどのコピーよりも素早くコピーできます。
具体的には、次のように配列の中身をコピーします。
// コピー元
int[] src = {1, 2, 3};
// コピー先
int[] dest = new int[src.length];
// コピー
System.arraycopy(src, 0, dest, 0, src.length);
ただし、コピーを行う配列がいわゆる2次元以上の配列の場合は注意が必要です。
Java では、2次元配列は「配列の配列」として表現します。つまり、配列オブジェクトを格納する配列の中に配列オブジェクトが入っているという考えです。
int[][] array;
// 異なる書き方で同じ容量の配列を確保
array = new int[2][2];
array = {new int[2], new int[2]};
配列オブジェクトは参照型です。System#arraycopy メソッドの場合、参照値はそのままコピーされます。つまり、コピー元とコピー先で入れ子になっている同じ配列オブジェクトを指すわけです。
int[][] src = {{1, 2}, {3, 4}};
int[][] dest = {{5, 6}, {7, 8}};
System.arraycopy(src, 0, dest, 0, 2);
System.out.println(src[0] == dest[0]);
上記の出力は true となります。もしも入れ子になった配列オブジェクトの中身がコピーされていれば、src[0] と dest[0] は同一のインスタンスではないため、 false となるはずです。参照がコピーされたために、src[0] と dest[0] は同一のインスタンスを指すようになり、 true となったわけです。
これではコピーできたとは言えません。src[0][0] の値を書き換えると、dest[0][0] の中身も変わってしまいます。
そこで、入れ子になっている配列の中身をコピーする場合は、その配列を直接 System#arraycopy メソッドに渡すようにします。
int[][] src = {{1, 2}, {3, 4}};
int[][] dest = {{5, 6}, {7, 8}};
for (int i = 0; i < src.length; i++) {
System.arraycopy(src[i], 0, dest[i], 0, 2);
}
System.out.println(src[0] == dest[0]);
この場合、出力は false となります。