Files
ECC/docs/ja-JP/skills/tinystruct-patterns/references/database.md
T

9.3 KiB
Raw Blame History

tinystruct データベース永続化

使用する場面

データベース操作にはフレームワーク組み込みのORM的なデータ層を使用します。これは AbstractData を拡張したPOJOを、アノテーションまたはXMLマッピングファイルでテーブルにマッピングし、JPA/Hibernateの軽量な代替として機能します。

動作の仕組み

アーキテクチャ

各テーブルは以下によって表されます:

  1. Java POJO:AbstractData を拡張し、getter/setter と setData(Row) を提供する。
  2. マッピング、いずれか一方:
    • アノテーション:クラスに @Table、フィールドに @Column(アノテーションマッピングを参照)。
    • マッピングXML:リソースに置かれた ClassName.map.xml で、Javaフィールドとデータベースカラムを結びつける。

マッピングはクラスごとに一度だけ読み込まれてキャッシュされるため、インスタンスの生成で再読み込みや再解析は行われません。クラスに @Table が存在する場合、同名の .map.xml より優先されます。

主要な基底クラス:AbstractData

CRUDメソッドを提供します:

  • append() / appendAndGetId()
  • update()
  • delete()
  • findAll() / findOneById() / findOneByKey(key, value)
  • findWith(where, params)
  • find(SQL, params)

POJOの生成(CLI)

ライブのデータベーステーブルを内省してPOJOとそのマッピングを生成します。デフォルトでは .map.xml ファイルが、annotation オプションを指定するとPOJO自体にアノテーションが付与されます。

設定

application.properties:

driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/mydb
database.user=root
database.password=secret

コマンド

# インタラクティブモード
bin/dispatcher generate

# テーブルを指定
bin/dispatcher generate --tables users

# .map.xml ではなくPOJOにアノテーションを付与
bin/dispatcher generate --tables users --mapping annotation

--mapping は xml(デフォルト)または annotation を受け付けます。アノテーションモードでは .map.xml は書き出されず、リソースディレクトリも作成されません。不明な値を指定するとプロンプトの前にエラーで拒否されます。

例

CRUD操作

// CREATE
User user = new User();
user.setUsername("james");
user.append();

// READ
User user = new User();
user.setId(42);
user.findOneById();

// UPDATE
user.setEmail("new@example.com");
user.update();

// DELETE
user.delete();

条件を使ったクエリ

User user = new User();
Table results = user.findWith("username LIKE ?", new Object[]{"%jam%"});

// 流暢なConditionビルダー
Condition condition = new Condition();
condition.setRequestFields("id,username");
Table filtered = user.find(
    condition.select("`users`").and("email LIKE ?").orderBy("id DESC"),
    new Object[]{"%@example.com"}
);

マッピングXMLの構造

User.map.xml:

<mapping>
  <class name="User" table="users">
    <id name="Id" column="id" autoIncrement="true" autoGenerated="false" length="11" type="int"/>
    <property name="username" column="username" length="50" type="varchar"/>
    <property name="email" column="email" length="100" type="varchar"/>
  </class>
</mapping>

アノテーションマッピング

アノテーションを使った同じ User(generate --mapping annotation が生成するもの):

import org.tinystruct.data.annotation.Column;
import org.tinystruct.data.annotation.Id;
import org.tinystruct.data.annotation.Table;
import org.tinystruct.data.component.AbstractData;
import java.io.Serializable;

@Table(name = "users",
        id = @Id(name = "Id", column = "id", type = "int", length = 11, autoIncrement = true))
public class User extends AbstractData implements Serializable {
    @Column(name = "username", type = "varchar", length = 50)
    private String username;

    @Column(name = "email", type = "varchar", length = 100)
    private String email;

    // getter、setter(setFieldAsXxx を使用)、および setData(Row) を通常通り実装
}
アノテーション 対象 属性
@Table クラス name(必須)、schema(デフォルト:なし)、id(デフォルト:識別子なし)
@Id @Table(id = ...) 内 name(デフォルト Id)、column、type、length、autoIncrement、autoGenerated。column が空の場合、テーブルに識別子がないことを意味する。
@Column フィールド type(必須)、name(DBカラム名。デフォルトはJavaフィールド名)、length
  • 識別子は AbstractData から継承されるため、独自のフィールドに @Id でアノテーションするのではなく @Table 内で記述する。
  • type はXMLの type 属性と同じ名前(FieldType の名前、大文字小文字を無視してマッチング)を取る:INT、BIGINT、VARCHAR、TEXT、DATE、TIMESTAMP など。
  • @Column が付与されたフィールドのみマッピングされる。@Table は継承され、スーパークラスのアノテーション付きフィールドもマッピングされる。
  • 同じプロパティを二重にマッピング(例:スーパークラスのアノテーション付きフィールドを隠すサブクラスフィールド)すると、そのクラスが最初に使われる際にエラーになる。
  • autoGenerated = true は整数でないIDに新規インスタンスごとにUUIDを付与し、autoIncrement = true はその値をデータベースに委ねる。
  • .map.xml の <id> 要素も同じ名前 autoIncrement・autoGenerated を使う。旧来の increment・generate 属性で書かれたファイルも引き続き動作する。両方の綴りが存在する場合、新しい方が優先される。

テーブルの自動作成

application.properties に1行追加することで、存在しないテーブルを自動的に作成できます:

database.autocreate=true
  • デフォルトはオフ。 これをオンにしない限り、スキーマは何も変更されません。オンにする場合、データベースユーザーは CREATE 権限が必要です。
  • マッピングされたクラスが最初にインスタンス化される際に、テーブルが存在しなければ作成されます。これはプロセスごと・クラスごとに一度だけ発生し、アノテーションとXMLマッピングの両方で機能し、以降のインスタンスには何も追加されません。
  • CREATE TABLE IF NOT EXISTS という単一ステートメントが使用され、複数のインスタンスが同時に起動しても安全です(SQL Server では OBJECT_ID ガードが使われます)。
  • @Id カラムが主キーになり、autoIncrement = true の場合はデータベース固有のオートインクリメントまたはIDENTITY構文が使われます。
  • カラム型はターゲットデータベース固有の型にレンダリングされます(例:TIMESTAMP はMySQLでは DATETIME、SQL Serverでは DATETIME2、TEXT はSQL Serverでは VARCHAR(MAX)、BLOB はPostgreSQLでは BYTEA)。length なしの VARCHAR は255になります。
  • 失敗(例:権限不足)は警告としてログに記録され、オブジェクトの生成は停止しません。最初の実際のステートメントでデータベース固有のエラーが報告されます。

注意点:

  • DECIMAL カラムはマッピングが単一の length のみを持つため、精度やスケールなしで作成されます。
  • FieldType の名前に含まれないカラム型は拒否されます。
  • テーブル、カラム、主キーのみが作成されます。インデックス、外部キー、デフォルト値、他カラムへの NOT NULL は作成されません。
  • 既存のテーブルはマッピングと異なっていても変更・マイグレーションされません。
  • Redisはテーブルを持たず、サポートされていません。

重要なルール

  1. ファイルの配置(XMLマッピングのみ):マッピングXMLは src/main/resources/ 以下にPOJOのパッケージパスを反映した場所に置く必要があります。
  2. 命名規則:テーブル名はクラス名に単数化されます(users → User)。アンダースコアのカラムはキャメルケースのフィールドになります(created_at → createdAt)。
  3. セッター:セッターでは(例:setFieldAsString)setFieldAsXxx メソッドを使用して内部フィールドマップと状態を同期させてください。
  4. Idフィールド:Javaでの主キーフィールドは常に Id という名前です(AbstractData から継承)。
  5. マッピングソースは1つ:クラスに対してアノテーションかXMLのどちらかを使用してください。両方が存在する場合、アノテーションが優先されてXMLは無視されます。