4.1. 型付けされたテーブルオブジェクト
この章では、WebSQLTable の新機能について説明します。

LSJ の新機能では、テーブルオブジェクトの構造を、ジェネリックス による型指定で定義できるようになりました。
これにより、データ型について安全なコードが よりシンプルなコーディングで実装可能になります。
また、項目指定にデータベース物理カラム名が不要となるため、DB 依存性も低下します。

データ構造を定義したクラス(エンティティクラス)をテーブルクラスに指定することで、特定のエンティティ型に対応付けされた
テーブルオブジェクトを利用できるようになります。
エンティティクラスによる型付け

Employeeクラスのテーブルオブジェクトを作成する

Table クラスの tableEmp を定義する際に、「<」「>」を記述して エンティティを定義した Employee クラスを型付けします。
/* Employeeクラスのテーブルオブジェクトを作成する */
public Table<Employee> tableEmp = new Table<Employee>();
public 宣言することにより、テンプレートからのアクセスが可能になります。

データ項目の参照

宣言した変数に値を設定します。
/* データ項目の参照 */
Employee emp = tableEmp.entity;
System.out.println("社員ID:" + emp.id );
System.out.println("社員名:" + emp.name );
tableEmp.entity でカーソル行のエンティティオブジェクトへのショートカットを表します。
tableEmp.getEntity(); で取得できるオブジェクトと同じ内容です。
また、Row クラスを Employee クラスで型付けすることも可能です。

データ項目の設定

宣言した変数に値を設定します。
/* データ項目の設定 */
Employee emp = tableEmp.startEditing();
emp.id = 1234;
emp.name = "熊谷 曜子";
tableEmp.validate(emp);
※従来のメソッドも全て利用可能です。
エンティティクラスの作成
引数なしコンストラクタを持つ Java クラスをエンティティクラスとして指定することができます。
エンティティクラスの中で、以下のフィールドまたはメソッドがテーブルオブジェクトのカラムとして扱われます。
1. 可視性が public のデータフィールド
    ※ int, long等のプリミティブ型は使用不可、Integer, Long等のラッパークラスを使用する )

2. 可視性が public の getter, setter メソッド
    ※ getter のみ定義している場合は、読取専用の項目として扱われます。
        getter なし(setter のみを定義)はカラムとして扱われません。

getter, setter メソッドは以下のような計算式でも構いません。
/* Workクラスを定義 */
public static class Work {
    public Integer finishTimeM = new Integer(0);
    public Integer beginTimeM = new Integer(0);
    public Integer breakTimeM = new Integer(0);

    // workTimeM を計算式で定義します
    public Integer getWorkTimeM() { return finishTimeM - beginTimeM - breakTimeM; }
}
データベーステーブルとのマッピング
エンティティクラス とデータベーステーブルは、以下のルールで対応付け(マッピング)が自動的に行われます。

1. エンティティクラス名、フィールド名、メソッド名("set","get" )から、
    データベース内のテーブル名、カラム名を自動的に決定します。
    ※駱駝表記の java オブジェクト名をアンダースコア区切りの DB オブジェクト名に自動変換します。

2. java コードと DB のデータ型対応付けは jp.littlesoft.data.Column.classToColtypeMap
    定義されています。


以下エンティティクラスEmployeeはDBテーブル EMPLOYEEと対応付けられます。
public static class Employee {
  public Long id;
  public Integer deptCode;
  public String name;
  public Date enterDate;
  public Short sex;
  public BigDecimal salary;
}
CREATE TABLE EMPLOYEE (
  ID BIGINT NOT NULL,
  DEPT_CODE INTEGER NOT NULL,
  NAME VARCHAR(50) NOT NULL,
  ENTER_DATE DATE NOT NULL,
  SEX SMALLINT NOT NULL,
  SALARY NUMERIC(9 , 2) NOT NULL
);
名前の自動変換では、うまくマッピングできない場合、以下のように TableDef アノテーション、ColumnDef アノテーションで DB オブジェクト名を明示することができます。
// Employee を T_EMPテーブルにマッピングします
@TableDef(name="T_EMP")
public static class Employee {
    // id を EMP_ID にマッピングします
    @ColumnDef(name="EMP_ID")
    public Long id;
        :
}
アノテーションでは、nameの他に caption, formatPattern, defaultValue, nullableなどが指定できます。
@TableDef(schema="PUBLIC", name="T_EMP")
public static class Employee {
    @ColumnDef(name="EMP_ID", caption="社員ID")
    public Long id;
    @ColumnDef(caption="所属")
    public Integer deptCode;
    @ColumnDef(caption="氏名")
    public String name;
    @ColumnDef(caption="入社日", formatPattern="yyyy/MM/dd")
    public Date enterDate;
    @ColumnDef(caption="性別", defaultValue="1")
    public Short sex;
    @ColumnDef(caption="給与", formatPattern="#,##0.00;-#,##0.00")
    public BigDecimal salary;
}
エンティティクラスの生成ツール
jp.littlesoft.tools.SQLEntityClassWriter を利用するとデータベーススキーマをもとにエンティティクラスの java ソースを自動生成することができます。
SQLEntityClassWriter は Java アプリケーションです。 java コマンドラインで以下のような引数を設定して実行してください。
jp.littlesoft.tools.SQLEntityClassWriter d=org.h2.Driver u=jdbc:h2:C:/mydb i=sa s=PUBLIC

引数

d: JDBCドライバー名
u: JDBC 接続 URL
i: USER 名
p: パスワード
s: 対象スキーマ名
f: 出力ファイル名

出力サンプル

public class DB implements Serializable {
    @TableDef(name="DEPARTMENT")
    public static class Department implements Serializable {
        @ColumnDef(name="CODE")
        public Integer code;
        @ColumnDef(name="NAME")
        public String name;
        @ColumnDef(name="PARENT_CODE")
        public Integer parentCode;
    }

    @TableDef(name="EMPLOYEE")
    public static class Employee implements Serializable {
        @ColumnDef(name="EMP_ID")
        public Long empId;
        @ColumnDef(name="DEPT_CODE")
        public Integer deptCode;
        @ColumnDef(name="NAME")
        public String name;
        @ColumnDef(name="ENTER_DATE")
        public Date enterDate;
        @ColumnDef(name="SEX")
        public Short sex;
        @ColumnDef(name="SALARY")
        public BigDecimal salary;
        @ColumnDef(name="MEMO")
        public String memo;
        @ColumnDef(name="CREATE_DATETIME")
        public Timestamp createDatetime;
        @ColumnDef(name="UPDATE_DATETIME")
        public Timestamp updateDatetime;
    }
エンティティクラスからデータベーススキーマを生成する
jp.littlesoft.sql.SchemaUpdator クラスを利用すると、DDLを作成せずに、直接エンティティクラス情報からデータベースのスキーマを生成することが出来ます。

1.エンティティクラスにITableDefインターフェースを実装し、メタ情報を定義する。

createMetaData メソッドをオーバーライドすると、メタ情報を生成するコードを実装するようになります。
下記のコード例では、addPrimary メソッドで主キーの定義を追加しています。
public class DB {
    public static class UserGroup implements ITableDef {
        @ColumnDef(caption="グループID", nullable=Nullable.FALSE )
        public Integer id;
        @ColumnDef(caption="グループ名", length=80, nullable=Nullable.FALSE)
        public String name;

        // createMetaDataメソッドをオーバーライドし、このエンティティのメタ情報を生成するコードを実装します
        @Override
        public MetaData<?> createMetaData() {
            return new MetaData<UserGroup>() {{
                // ここでは主キーの定義を行っています
                addPrimaryKey($$.id);
            }};
        }
    }
createMetaData メソッドでは、主キーの定義の他にも外部キー、インデックスの定義などが行えます。
public static class User implements ITableDef {
    @ColumnDef(caption="ユーザーID", length=32, nullable=Nullable.FALSE )
    public String id;
    @ColumnDef(caption="ユーザー名", length=80, nullable=Nullable.FALSE)
    public String name;
    @ColumnDef(caption="グループID", nullable=Nullable.TRUE )
    public Integer groupId;
    // createMetaDataメソッドをオーバーライドし、このエンティティのメタ情報を生成するコードを実装します
    @Override
    public MetaData<?> createMetaData() {
        return new MetaData<USer>() {{
            addPrimaryKey($$.id);
            // 外部キーの定義を行います
            addForeginKey($$.groupId)
                // 参照先テーブルのエンティティクラスを指定します
                .references(Group.class)
                // onDelete、onUpdateのオプション設定も可能
                .onDelete(FKOption.RESTRICT)
                .onUpdate(FKOption.SET_NULL)
                // 外部キーにインデックスを指定する場合はtrueを指定します
                .usingIndex(true);
            // NAMEに対する単独インデックスを定義します。
            addIndex($$.name);
        }};
    }
}

2.SchemaUpdatorクラスを使用して、データベーススキーマを生成する。

以下のように createOrAlterTable メソッドを使用すると、スキーマを作成します。
引数は作成対象のエンティティクラスです。
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:C:/sample/lsjsample", "sa", "");
new SchemaUpdator(conn).createOrAlterTable(
    // 作成対象のエンティティクラス列挙します
    DB.UserGroup.class,
    DB.User.class
);
これにより以下のDDLが生成されデータベースに適用されます。
CREATE TABLE USER_GROUP (
    ID INTEGER NOT NULL
    ,NAME VARCHAR(80) NOT NULL
);

ALTER TABLE USER_GROUP ADD CONSTRAINT USER_GROUP_PK PRIMARY KEY (ID);

CREATE TABLE USER (
    ID VARCHAR(32) NOT NULL
    ,NAME VARCHAR(80) NOT NULL
    ,GROUP_ID INTEGER NULL
);

ALTER TABLE USER ADD CONSTRAINT USER_PK PRIMARY KEY (ID);
CREATE INDEX USER_IX4cd4defb ON USER(GROUP_ID);
CREATE INDEX USER_IX24728b ON USER(NAME);
ALTER TABLE USER ADD CONSTRAINT USER_FKb69ca66f
    FOREIGN KEY (GROUP_ID) REFERENCES USER_GROUP (ID) ON UPDATE SET NULL;

3.スキーマの修正、変更を行う。

エンティティクラスの修正・変更を行った後、createOrAlterTable メソッドを実行すると
データベーススキーマとの差分を検出し、変更DDLを自動的に適用します。
// 桁の変更(length=80 → 120)
@ColumnDef(caption="グループ名", length=120, nullable=Nullable.FALSE)
public String name;

// データ型の変更(String → Long)
@ColumnDef(caption="ユーザーID", nullable=Nullable.FALSE )
public Long id;

// カラム名の変更(GROUP_ID → GID)
@ColumnDef(oldName="GROUP_ID" caption="グループID", nullable=Nullable.TRUE )
public Integer gid;
// アノテーション(oldName)で変更前の物理カラム名を指定します

// createOrAlterTableメソッドを再実行
new SchemaUpdator(conn).createOrAlterTable(
    UserGroup.class,
    User.class,
);
実行すると、以下のようなALTER文が生成・適用されます。
// グループ名 の桁変更
ALTER TABLE USER_GROUP ALTER COLUMN NAME VARCHAR(120) NOT NULL;
// ユーザID の型変更
ALTER TABLE USER ALTER COLUMN ID BIGINT NOT NULL;
// グループID の物理カラム名変更
ALTER TABLE USER ALTER COLUMN GROUP_ID RENAME TO GID;
※ 上記の変更以外にも、DECIMAL 型の Presision、Scale、NULL制約、デフォルト値の変更に対応します。