テーブルの作成(1)
記事テーブル(article)の作成
テーブルの設計
今回作成するのは複数のスレッドが作成できる掲示板システムです。まず以下のような記事テーブル(article)を作成します。記事IDはシーケンスで自動生成される主キーで、スレッドを作成した場合「記事ID=スレッドID」のレコードを作成するようにします。スレッド内の記事を登録する場合、すでに存在するスレッドIDを指定して記事を登録します。スレッドIDを指定し記事ID順にArticleTableからレコードを取得すれば、スレッド内の記事一覧ができあがるという仕組みにします。
| カラム名 | データ型 | 備考 |
|---|---|---|
| article_id | bigint not null | 記事ID(自動生成される主キー) |
| thread_id | bigint | スレッドID(記事ID=スレッドIDの場合スレッドの先頭記事) |
| title | varchar(256) | 記事タイトル |
| auther | varchar(64) | 著者 |
| contents | varchar(4096) | 本文 |
| delete_flag | char(1) | 削除フラグ |
| create_user_id | bigint not null | 作成者ID |
| create_timestamp | timestamp not null | 作成日時 |
| update_user_id | bigint not null | 更新者ID |
| update_timestamp | timestamp not null | 更新日時 |
テーブルJavaクラスの作成
dataforms.jarでは、テーブルの定義もJavaで行います。開発ツールの[テーブルJavaクラス作成]を使うと、テーブルクラスとそれに配置するフィールドクラスを効率よく作成することができます。テーブルJavaクラス作成のページを表示し、[新規作成]ボタンを押下すると、以下のページが表示されます。
まず[機能]のプルダウンで「/bbs」を選択します。すると[パッケージ]に「bbs.dao」という文字列が表示されます。これはテーブルクラスを作成するパッケージになります。通常はこのパッケージを使用しますが、変更することも可能です。[テーブルクラス名]には「ArticleTable」と入力します。通常、テーブルクラス名には「AaaBbbTable」のように「Table」で終わる文字列を指定します。「AaaBbbTable」テーブルクラスを使用してデータベースにテーブルを作成すると「aaa_bbb」というテーブルが作成されます。[上書モード]は「上書きはエラー」のままにしておきます。[主キー自動生成]はチェックしておきます。
フィールド一覧の[+]ボタンを押下し、フィールドを追加していきます。記事テーブル(article)を作成するには以下のようにフィールドを入力します。
一般的なフィールド定義は、dataforms.jar中に定義されているフィールドクラスから派生した新たなクラスを作成し、そのクラスのインスタンスをテーブルに配置する作業になります。
フィールドの行を追加すると、[パッケージ]には「bbs.field」がデフォルトで設定されています。フィールドのクラス名は「XxxYyyField」のように「Field」で終わる文字列を指定します。フィールドには常にフィールドIDが設定されます。フィールドIDを指定しない場合クラス名から自動的にデフォルトフィールドIDが設定されます。「XxxYyyField」クラスの場合、「xxxYyy」がデフォルトフィールドIDとなります。また、フィールドIDが「xxxYyy」の場合、対応するデータベーステーブルのカラム名は「xxx_yyy」となります。
[基本クラスパッケージ]には「dataforms」がデフォルトで設定されています。[基本クラスパッケージ]を変更せず、[基本クラス]にdataforms.jar中のフィールドクラス名を入力すると、jQuery UIのオートコンプリート機能で存在するクラスの一覧が表示されます。一覧から使用したいクラスを選択すると、[基本クラスパッケージ]が適切に設定されます。
主キーとなるArticleIdFieldクラスはRecordIdFieldから派生しています。主キー自動生成が指定されたテーブルで、主キーがRecordIdFieldから派生したフィールドの場合、レコードの追加時にシーケンスから自動的に生成されます。
DeleteFlagFieldは、例外的にdataforms.jar中のテーブルクラスを派生せずに直接使用しています。[パッケージ]を「dataforms」に変更し、[クラス]に「Delete」と入力すると、オートコンプリート機能で入力候補が表示されます。一覧から選択すれば簡単に、dataforms.jar中のフィールドクラスを指定することができます。
DeleteFlagFieldが存在するテーブルでは、問い合わせ実行時に「delete_flag='0'」の条件式が自動的に生成されます(この機能はOFFにすることも可能です)。
このように入力された情報からテーブルJavaクラスを作成すると、以下のソースファイルが作成されます。
| パッケージ | クラス | 備考 |
|---|---|---|
| bbs.dao | ArticleTable.java | 記事テーブルクラス |
| ArticleTableRelation.java | 記事テーブルと他のテーブルとの関係を定義するクラス | |
| bbs.field | ArticleIdField.java | 記事IDフィールドクラス |
| ThreadIdField.java | スレッドIDフィールドクラス | |
| TitleField.java | 記事IDフィールドクラス | |
| AutherField.java | 著者フィールドクラス | |
| ContentsField.java | 本文フィールドクラス |
生成された「ArticleTable.java」の内容は以下のようになります。
package bbs.dao;
・・・ 中略 ・・・
/**
* 記事テーブルクラス。
*
*/
public class ArticleTable extends Table {
/**
* コンストラクタ。
*/
public ArticleTable() {
this.setAutoIncrementId(true);
this.setComment("記事テーブル");
this.addField(new ArticleIdField()); //記事ID
this.addField(new ThreadIdField()); //スレッドID
this.addField(new TitleField()); //記事タイトル
this.addField(new AutherField()); //著者
this.addField(new ContentsField()); //本文
this.addField(new DeleteFlagField()); //削除フラグ
this.addUpdateInfoFields();
}
@Override
public String getJoinCondition(final Table joinTable, final String alias) {
ArticleTableRelation r = new ArticleTableRelation(this);
return r.getJoinCondition(joinTable, alias);
}
/**
* Entity操作クラスです。
*/
public static class Entity extends dataforms.dao.Entity {
/** 記事IDのフィールドID。*/
public static final String ID_ARTICLE_ID = "articleId";
・・・ 中略 ・・・
/** 削除フラグのフィールドID。*/
public static final String ID_DELETE_FLAG = "deleteFlag";
/**
* コンストラクタ。
*/
public Entity() {
}
/**
* コンストラクタ。
* @param map 操作対象マップ。
*/
public Entity(final Map<String, Object> map) {
super(map);
}
/**
* 記事IDを取得します。
* @return 記事ID。
*/
public java.lang.Long getArticleId() {
return (java.lang.Long) this.getMap().get(Entity.ID_ARTICLE_ID);
}
/**
* 記事IDを設定します。
* @param articleId 記事ID。
*/
public void setArticleId(final java.lang.Long articleId) {
this.getMap().put(Entity.ID_ARTICLE_ID, articleId);
}
・・・ 中略 ・・・
}
/**
* 記事IDフィールドを取得します。
* @return 記事IDフィールド。
*/
public ArticleIdField getArticleIdField() {
return (ArticleIdField) this.getField(Entity.ID_ARTICLE_ID);
}
/**
* スレッドIDフィールドを取得します。
* @return スレッドIDフィールド。
*/
public ThreadIdField getThreadIdField() {
return (ThreadIdField) this.getField(Entity.ID_THREAD_ID);
}
・・・ 中略 ・・・
}
ArticleTableのコンストラクタで、各フィールドを配置しています。コンストラクタの最後にある「this.addUpdateInfoFields();」で作成者ID、作成日時、更新者ID、更新日時の定番フィールドが配置されます。作成日時はCreateTimestampFieldクラス、更新日時はUpdateTimestampFieldクラスのフィールドが配置され、テーブルに対するInsertやUpdateの際、適切にタイムスタンプが設定されるようになります。また、これらのフィールドが不要であれば削除しても問題ありません。
基本的にデータベース中のデータは、マップ(Map<String, Object>)の形式でやり取りします。マップを直接使用すると、文字列でフィールドIDを指定するため、そのフィールドIDやデータ型のキャストを間違える可能性が高くなります。そのため、Entityというインナークラスに、フィールドIDの定数やテーブルに対応したマップを操作するためのメソッドを用意しました。マップの操作にEntityクラスを使用すると、EclipseのJavaエディターの入力補助機能が効くので、IDの間違いやデータのキャストミスを回避することができます。
ArticleTableのgetJoinConditionというメソッドではArticleTableRelationクラスのgetJoinConditionメソッドを呼び出しています。このメソッドで、他のテーブルとJOINする際の条件式を作成します。ArticleTableとその他のテーブル間の関係は、ArticleTableRelationクラスで定義します。「ArticleTableRelation.java」は基本的にテキストエディターで、結合条件を記述していきます。「ArticleTableRelation.java」に記述した後で、[テーブルJavaクラス作成]でテーブルクラスを呼び出し、テーブルの構造を修正することができます。その際ArticleTable.javaは更新しますが、ArticleTableRelation.javaの更新は行わないようになっています。「XxxTable.java」はテキストエディターでの編集も可能ですが、基本的に[テーブルJavaクラス作成]で編集するソースだと思ってください。
生成されたフィールドクラスの例として、「ContentsField.java」を以下に示します。
package bbs.field;
import dataforms.field.sqltype.VarcharField;
import dataforms.validator.MaxLengthValidator;
/**
* ContentsFieldフィールドクラス。
*
*/
public class ContentsField extends VarcharField {
/**
* フィールド長。
*/
private static final int LENGTH = 4096;
/**
* フィールドコメント。
*/
private static final String COMMENT = "本文";
/**
* コンストラクタ。
*/
public ContentsField() {
super(null, LENGTH);
this.setComment(COMMENT);
}
/**
* コンストラクタ。
* @param id フィールドID。
*/
public ContentsField(final String id) {
super(id, LENGTH);
this.setComment(COMMENT);
}
@Override
protected void onBind() {
super.onBind();
this.addValidator(new MaxLengthValidator(this.getLength()));
}
}
フィールドクラスにはデフォルトコンストラクタとフィールドIDを引数に持つ2種類のコンストラクタが生成されています。デフォルトコンストラクタContentsField()を使用した場合、そのフィールドのIDは「contents」となります。[テーブルJavaクラス作成]のフィールドID欄は、同じクラスのフィールドを複数回配置する場合に使用します。フィールドIDに「contents2」と指定された場合、テーブルクラスコンストラクタにはthis.addField(new ContentsField("contents2"));の行が追加され、フィールドID指定のコンストラクタが呼ばれます。
onBindメソッドはフィールドクラスのインスタンスをフォームに配置したタイミングで呼ばれます。この処理では、フォームの入力時に入力された最大文字数をチェックするためのバリデータを登録しています。バリデータはフォームでのみ動作し、テーブルに配置するフィールドでは不要なので、onBindで追加するようになっています。
開発ツールでのソース生成後の操作について
開発ツールで各種ソースファイルを生成すると、ワークスペースの指定フォルダに「*.java」ファイルが作成されます。[テーブルJavaクラス作成]等は既存のテーブルクラスを検索し、それを再編集することができます。ただし、これらのツールはコンパイルされた「*.class」ファイルが検索対象です。そのため開発ツールで各種ソースを生成したら、すぐにビルド+デプロイし、Webアプリケーションに「*.class」を認識させる必要があります。Eclipse+Tomcat 8の環境では、クリーンビルドし、オートデプロイさせればOKです。
データベースのテーブル作成
「ArticleTable.java」で定義した記事テーブルをデータベース上に作成するには開発ツールの[テーブル管理]を使用します。[テーブル管理]の[機能]プルダウンで「/bbs」を選択し検索すると、以下のように一覧にArticleTableが表示されます。背景が赤く表示されているのはデータベース上に対応するテーブルが存在しないことを意味しています。
このテーブルをチェックし[初期化]ボタンを押下すると、以下のSQLが実行され、テーブルが作成されます。このSQLは接続しているデータベースシステムに適したものになります。今回はApache Derbyに接続しているので、Apache Derby用のSQLになります。
create table article ( article_id bigint not null ,thread_id bigint ,title varchar(256) ,auther varchar(64) ,contents varchar(4096) ,delete_flag char(1) default '0' ,create_user_id bigint not null ,create_timestamp timestamp not null ,update_user_id bigint not null ,update_timestamp timestamp not null ,primary key(article_id) );
