Doma CodeGen プラグイン

概要

Doma CodeGenプラグイン は Gradle プラグインです。データベーススキーマから Java、Kotlin、および SQL ファイルを生成します。

主な利点

  • データベースファーストな開発: 既存のデータベーススキーマから型安全なエンティティクラスとDAOクラスを直接生成

  • マルチ言語対応: 同じ設定でJavaとKotlinの両方のコードを生成

  • SQLテンプレート生成: 一般的なREAD操作のSQLテンプレートファイルを自動作成

  • Testcontainers統合: データベーステストとコード生成でTestcontainersとシームレスに連携

  • カスタマイズ可能: カスタムテンプレートを使用して生成コードの構造とスタイルを制御

  • 複数データベース対応: PostgreSQL、MySQL、Oracle、H2、その他のJDBC互換データベースに対応

使用例

  • 高速プロトタイピング: データベース設計からデータアクセス層を素早く構築

  • スキーマ進化: データベーススキーマの変更にあわせてコードを同期

  • チーム開発: チームメンバー間でエンティティとDAOの実装を一貫性のあるものにする

AntベースのDoma-Genのドキュメントをお探しですか?

AntベースのDoma-Genのドキュメントは、 Doma-Gen GitHub リポジトリ にあります。

AntベースのDoma-Genは現在メンテナンスされていません。代わりにこのページで説明されているDoma CodeGenプラグインを使用してください。

始め方

前提条件

  • Gradle 8.0 以上

  • Java 17 以上

  • データベースへのアクセス(ローカル、リモート、またはTestcontainersベースが可能)

ステップバイステップの設定

  1. プラグインの追加

    Doma CodeGenプラグインをGradleビルドファイルに追加します:

    plugins {
        java
        id("org.domaframework.doma.codegen") version "3.2.2"
    }
    
  2. 依存関係の設定

    必要なJDBCドライバーの依存関係を追加します:

    dependencies {
        // For code generation
        domaCodeGen("org.postgresql:postgresql:42.7.7")
    }
    
  3. ローカルPostgreSQLデータベースのセットアップ

    PostgreSQLがローカルにインストールされ実行されていることを確認してください。データベースとテーブルを作成します:

    -- Connect to PostgreSQL and create database
    CREATE DATABASE myapp;
    
    -- Switch to the new database and create tables
    CREATE TABLE users (
        id SERIAL PRIMARY KEY,
        name VARCHAR(100) NOT NULL,
        email VARCHAR(255) UNIQUE NOT NULL,
        version INTEGER NOT NULL DEFAULT 1,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    
    CREATE TABLE orders (
        id SERIAL PRIMARY KEY,
        user_id INTEGER REFERENCES users(id),
        total_amount DECIMAL(10,2) NOT NULL,
        order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        version INTEGER NOT NULL DEFAULT 1
    );
    
  4. コード生成の設定

    完全なbuild.gradle.ktsの例:

    plugins {
        java
        id("org.domaframework.doma.codegen") version "3.2.2"
    }
    
    dependencies {
        // Code generation dependencies
        domaCodeGen("org.postgresql:postgresql:42.7.7")
    }
    
    domaCodeGen {
        val basePackage = "com.example.myapp"
    
        register("postgresql") {
            // Database connection to local PostgreSQL
            url.set("jdbc:postgresql://localhost:5432/myapp")
            user.set("postgres")  // Replace with your PostgreSQL username
            password.set("password")  // Replace with your PostgreSQL password
    
            // Entity generation settings
            entity {
                packageName.set("$basePackage.entity")
                useAccessor.set(true)           // Generate getters/setters
                useListener.set(true)           // Generate entity listeners
                showDbComment.set(true)         // Include database comments
            }
    
            // DAO generation settings
            dao {
                packageName.set("$basePackage.dao")
            }
        }
    }
    
  5. コードの生成

    コード生成タスクを実行します:

    $ ./gradlew domaCodeGenPostgresqlAll
    

    これにより以下が生成されます:

    • src/main/java/com/example/myapp/entity/ にエンティティクラス

    • src/main/java/com/example/myapp/dao/ にDAOインターフェース

    • src/main/resources/META-INF/com/example/myapp/dao/ にSQLテンプレートファイル

    • src/test/java/com/example/myapp/dao/ にテストクラス

生成される内容

コード生成を実行すると、以下のファイルが見つかります:

エンティティクラス

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer id;

    public String name;

    public String email;

    @Version
    public Integer version;

    @Column(name = "created_at")
    public Timestamp createdAt;
}

DAOインターフェース

@Dao
public interface UserDao {
    @Select
    User selectById(Integer id);

    @Insert
    Result<User> insert(User entity);

    @Update
    Result<User> update(User entity);

    @Delete
    Result<User> delete(User entity);
}

SQLテンプレート

SELECT /*%expand*/* FROM users WHERE id = /* id */1

データベースの例

以下の例では、異なるデータベースタイプの設定方法を示します:

PostgreSQL

dependencies {
    domaCodeGen("org.postgresql:postgresql:42.7.7")
}

domaCodeGen {
    register("postgresql") {
        url.set("jdbc:postgresql://localhost:5432/mydatabase")
        user.set("myuser")
        password.set("mypassword")
        entity {
            packageName.set("com.example.postgresql.entity")
        }
        dao {
            packageName.set("com.example.postgresql.dao")
        }
    }
}

TestcontainersでのPostgreSQL

dependencies {
    domaCodeGen(platform("org.testcontainers:testcontainers-bom:1.21.2"))
    domaCodeGen("org.postgresql:postgresql:42.7.7")
    domaCodeGen("org.testcontainers:postgresql")
}

domaCodeGen {
    register("postgresql") {
        val initScript = file("src/main/resources/schema-postgresql.sql")
        url.set("jdbc:tc:postgresql:15:///test?TC_INITSCRIPT=file:${initScript.absolutePath}")
        user.set("test")
        password.set("test")
        entity {
            packageName.set("com.example.postgresql.entity")
        }
        dao {
            packageName.set("com.example.postgresql.dao")
        }
    }
}

TestcontainersでのMySQL

dependencies {
    domaCodeGen(platform("org.testcontainers:testcontainers-bom:1.21.2"))
    domaCodeGen("mysql:mysql-connector-java:8.0.33")
    domaCodeGen("org.testcontainers:mysql")
}

domaCodeGen {
    register("mysql") {
        val initScript = file("src/main/resources/schema-mysql.sql")
        url.set("jdbc:tc:mysql:8.0:///test?TC_INITSCRIPT=file:${initScript.absolutePath}")
        user.set("test")
        password.set("test")
        entity {
            packageName.set("com.example.mysql.entity")
        }
        dao {
            packageName.set("com.example.mysql.dao")
        }
    }
}

Gradle タスク

Doma CodeGen プラグインは以下のタスクを提供します。

  • domaCodeGenXxxAll - すべてを生成します。

  • domaCodeGenXxxDao - DAO ソースファイルを生成します。

  • domaCodeGenXxxDto - 結果セットのメタデータを読み、DTO ソースファイルを生成します。

  • domaCodeGenXxxEntity - エンティティソースファイルを生成します。

  • domaCodeGenXxxSql - SQLファイルを生成します。

  • domaCodeGenXxxSqlTest - SQL テストソースファイルを生成します。

上記のタスク名の Xxx 部分は、domaCodeGen ブロックの下で定義されているブロック名に置き換えられます。上記の使用例では、Postgresql 部分は postgresql ブロックに対応しています。

定義済みのタスク名をすべて確認するには、tasks タスクを実行します。

$ ./gradlew tasks

設定リファレンス

名前付き設定

domaCodeGen ブロックの下に名前付き設定を定義する必要があります。設定には任意の名前を選択できます。異なるデータベースや環境をサポートするために複数の設定を定義できます。

例:複数データベース設定

domaCodeGen {
    register("sales") {
        url.set("jdbc:postgresql://localhost:5432/sales")
        user.set("sales_user")
        password.set("sales_pass")
        entity {
            packageName.set("com.example.sales.entity")
        }
        dao {
            packageName.set("com.example.sales.dao")
        }
    }

    register("inventory") {
        url.set("jdbc:mysql://localhost:3306/inventory")
        user.set("inventory_user")
        password.set("inventory_pass")
        entity {
            packageName.set("com.example.inventory.entity")
        }
        dao {
            packageName.set("com.example.inventory.dao")
        }
    }
}

これによりデータベースごとに個別のタスクセットが生成されます:

$ ./gradlew domaCodeGenSalesAll      # Generate all for sales DB
$ ./gradlew domaCodeGenInventoryAll  # Generate all for inventory DB

メイン設定オプション

これらのオプションは、各名前付き設定ブロックのトップレベルで設定されます:

オプション

説明

例の値

既定値

url

JDBC connection URL to your database

jdbc:postgresql://localhost:5432/mydb

Required

user

Database username for authentication

myuser

Required

password

Database password for authentication

mypass

Required

dataSource

Custom data source class (advanced)

inferred from URL

codeGenDialect

Database dialect for SQL generation (advanced)

inferred from URL

catalogName

テーブルをフィルタリングするデータベースのカタログ名

sales_catalog

schemaName

テーブルをフィルタリングするデータベースのスキーマ名

public, dbo, hr

tableNamePattern

Regex pattern to include specific tables

user_.* (tables starting with "user_")

.* (all tables)

ignoredTableNamePattern

Regex pattern to exclude tables

temp_.* (ignore temp tables)

.*$.* (ignore system tables)

tableTypes

Types of database objects to include

["TABLE", "VIEW"] (include tables and views)

["TABLE"]

versionColumnNamePattern

Regex to identify version columns

VERSION([_]?NO)?

VERSION([_]?NO)?

languageType

Target programming language

LanguageType.JAVA または LanguageType.KOTLIN

LanguageType.JAVA

templateDir

カスタムFreeMarkerテンプレートを含むディレクトリ

file("$projectDir/custom-templates")

encoding

生成されるソースファイルのテキストエンコーディング

UTF-8, Shift_JIS

UTF-8

sourceDir

生成されたソースファイルの出力ディレクトリ

src/main/java, src/main/kotlin

言語に依存

resourceDir

生成されたSQLファイルの出力ディレクトリ

src/main/resources

src/main/resources

globalFactory

プラグインの動作をカスタマイズするエントリポイント

GlobalFactory のインスタンス

エンティティ設定

entity ブロックはエンティティクラスの生成方法を設定します。このブロックは名前付き設定内で定義する必要があります。

基本例

domaCodeGen {
    register("sales") {
        entity {
            packageName.set("com.example.sales.entity")
            useAccessor.set(true)           // Generate getters/setters
            useListener.set(true)           // Generate entity listeners
            showDbComment.set(true)         // Include database comments
            prefix.set("Sales")             // Add prefix to class names
        }
    }
}

高度な例

domaCodeGen {
    register("enterprise") {
        entity {
            packageName.set("com.enterprise.domain.entity")
            superclassName.set("com.enterprise.core.BaseEntity")    // Common base class
            listenerSuperclassName.set("com.enterprise.core.BaseEntityListener")
            useMetamodel.set(true)          // Generate metamodel classes
            useMappedSuperclass.set(true)   // Use @MappedSuperclass
            originalStatesPropertyName.set("originalStates")  // Property for @OriginalStates
            showTableName.set(false)        // Don't show @Table annotations
            showColumnName.set(false)       // Don't show @Column annotations
        }
    }
}

オプション

説明

既定値

overwrite

生成されたエンティティファイルを上書きするかどうか

true

overwriteListener

リスナーを上書きするかどうか

false

superclassName

生成されたエンティティクラスに共通のスーパークラス

listenerSuperclassName

生成されたエンティティリスナークラスに共通のスーパークラス

packageName

生成されたエンティティクラスのパッケージ名

"example.entity"

generationType

エンティティのIDの生成タイプ

GenerationType の列挙値

namingType

命名規則

NamingType の列挙値

initialValue

エンティティIDの初期値

allocationSize

エンティティIDの割り当てサイズ

showCatalogName

カタログ名を表示するかどうか

false

showSchemaName

スキーマ名を表示するかどうか

false

showTableName

テーブル名を表示するかどうか

true

showColumnName

カラム名を表示するかどうか

true

showDbComment

データベースのコメントを表示するかどうか

true

useAccessor

アクセッサーを使うかどうか

true

useListener

リスナーを使うかどうか

true

useMetamodel

メタモデルを使うかどうか

true

useMappedSuperclass

マップされたスーパークラスを使用するかどうか

true

originalStatesPropertyName

@OriginalStates アノテーションが付けられるプロパティ

entityPropertyClassNamesFile

エンティティプロパティのクラスの解決に使用されるファイル

prefix

エンティティクラスの接頭辞

suffix

エンティティクラスの接尾辞

DAO設定

dao ブロックはDAO(Data Access Object)インターフェースの生成方法を設定します。

基本例

domaCodeGen {
    register("sales") {
        dao {
            packageName.set("com.example.sales.dao")
            suffix.set("Repository")         // Use "Repository" instead of "Dao"
        }
    }
}

オプション

説明

既定値

overwrite

生成されたDAOファイルを上書きするかどうか

false

packageName

生成された DAO クラスのパッケージ名

"example.dao"

suffix

Dao クラスの接尾辞

"Dao"

SQL設定

sql ブロックはSQLテンプレートファイルの生成方法を設定します。

domaCodeGen {
    register("sales") {
        sql {
            overwrite.set(true)             // Overwrite existing SQL files
        }
    }
}

注釈

SQLファイルは src/main/resources/META-INF/<package>/dao/ ディレクトリに生成されます。これには selectById.sqlselectByIdAndVersion.sql のような基本的なREAD操作が含まれます。

オプション

説明

既定値

overwrite

生成された SQL ファイルを上書きするかどうか

true

SQLテスト設定

sqlTest ブロックはSQLテストファイルの生成を設定し、テスト用に異なるデータベースを使用できます。

例:個別テストデータベース

domaCodeGen {
    register("production") {
        // Main database configuration
        url.set("jdbc:postgresql://prod-db:5432/myapp")
        user.set("prod_user")
        password.set("prod_pass")

        // Test database configuration
        sqlTest {
            url.set("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1")
            user.set("sa")
            password.set("")
        }
    }
}

例:テスト用同一データベース

domaCodeGen {
    register("development") {
        url.set("jdbc:tc:postgresql:15:///test")
        user.set("test")
        password.set("test")

        sqlTest {
            // Uses same connection as main configuration
            // No need to specify url, user, password again
        }
    }
}

オプション

説明

既定値

url

JDBC URL for test database (can be different from main)

メイン設定と同じ

user

Database username for test database

メイン設定と同じ

password

Database password for test database

メイン設定と同じ

カスタム設定

Kotlinコードの生成

Kotlin コードを生成するには、languageType オプションに LanguageType.KOTLIN を指定します。

import org.seasar.doma.gradle.codegen.desc.LanguageType

...

domaCodeGen {
    register("dev") {
        url.set("jdbc:postgresql://localhost:5432/mydatabase")
        user.set("myuser")
        password.set("mypassword")
        languageType.set(LanguageType.KOTLIN)
        entity {
            packageName.set("org.example.entity")
        }
        dao {
            packageName.set("org.example.dao")
        }
    }
}

テンプレートのカスタマイズ

Doma CodeGenプラグインは Apache FreeMarker テンプレートを使用してコードを生成します。これらのテンプレートをカスタマイズして、プロジェクトのコーディング規約と要件に合わせることができます。

利用可能なテンプレート

デフォルトのテンプレートファイルは ソースコードリポジトリ にあります。

テンプレートファイル

目的

生成される出力

entity.ftl

エンティティクラス生成

JPAアノテーション付きのJava/Kotlinエンティティクラス

entityListener.ftl

エンティティリスナー生成

ライフサイクルコールバック用のエンティティリスナークラス

dao.ftl

DAOインターフェース生成

基本的なCRUDメソッドを持つDAOインターフェース

sqlTest.ftl

SQLテスト生成

SQLファイルを検証するテストクラス

selectById.sql.ftl

基本的なSELECT SQL

主キーによる選択のためのSQLファイル

selectByIdAndVersion.sql.ftl

楽観的ロックSQL

バージョンチェック付きの選択のためのSQLファイル

カスタムテンプレートの設定

  1. テンプレートディレクトリの作成

    your-project/
    ├── custom-templates/
    │   ├── entity.ftl
    │   ├── dao.ftl
    │   └── entityListener.ftl
    └── build.gradle.kts
    
  2. テンプレートディレクトリの設定

    domaCodeGen {
        register("mydb") {
            url.set("jdbc:postgresql://localhost:5432/mydb")
            user.set("user")
            password.set("pass")
            templateDir.set(file("$projectDir/custom-templates"))
            entity {
                packageName.set("com.example.entity")
            }
            dao {
                packageName.set("com.example.dao")
            }
        }
    }
    
  3. エンティティテンプレートのカスタマイズ

    カスタムアノテーションを追加するために custom-templates/entity.ftl を作成してください:

    <#-- Custom entity template with additional annotations -->
    package ${entityDesc.packageName};
    
    import java.io.Serializable;
    import org.seasar.doma.*;
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    import lombok.Data;
    
    /**
     * Entity for ${entityDesc.tableName} table.
     <#if entityDesc.comment??> * ${entityDesc.comment}</#if>
     */
    @Entity<#if entityDesc.tableName??>(table = @Table(name = "${entityDesc.tableName}"))</#if>
    @Data                              // Lombok annotation
    @JsonIgnoreProperties(ignoreUnknown = true)  // Jackson annotation
    public class ${entityDesc.simpleName} implements Serializable {
    
    <#list entityDesc.propertyDescs as property>
        <#if property.id>
        @Id
        <#if property.generationType??>
        @GeneratedValue(strategy = GenerationType.${property.generationType})
        </#if>
        </#if>
        <#if property.version>
        @Version
        </#if>
        <#if property.columnName??>
        @Column(name = "${property.columnName}")
        </#if>
        public ${property.propertyClassName} ${property.propertyName};
    
    </#list>
    }
    
  4. DAOテンプレートのカスタマイズ

    カスタムDAOメソッドのために custom-templates/dao.ftl を作成してください:

    <#-- Custom DAO template with additional methods -->
    package ${daoDesc.packageName};
    
    import org.seasar.doma.*;
    import org.springframework.transaction.annotation.Transactional;
    import java.util.List;
    import java.util.Optional;
    
    /**
     * DAO for ${daoDesc.entityDesc.simpleName}.
     */
    @Dao<#if daoDesc.configClassName??>(config = ${daoDesc.configClassName}.class)</#if>
    @Transactional  // Spring transaction annotation
    public interface ${daoDesc.simpleName} {
    
        @Select
        Optional<${daoDesc.entityDesc.simpleName}> selectById(${daoDesc.entityDesc.idPropertyDesc.propertyClassName} ${daoDesc.entityDesc.idPropertyDesc.propertyName});
    
        @Select
        List<${daoDesc.entityDesc.simpleName}> selectAll();
    
        @Select
        List<${daoDesc.entityDesc.simpleName}> selectByExample(${daoDesc.entityDesc.simpleName} example);
    
        @Insert
        Result<${daoDesc.entityDesc.simpleName}> insert(${daoDesc.entityDesc.simpleName} entity);
    
        @Update
        Result<${daoDesc.entityDesc.simpleName}> update(${daoDesc.entityDesc.simpleName} entity);
    
        @Delete
        Result<${daoDesc.entityDesc.simpleName}> delete(${daoDesc.entityDesc.simpleName} entity);
    
        @BatchInsert
        BatchResult<${daoDesc.entityDesc.simpleName}> batchInsert(List<${daoDesc.entityDesc.simpleName}> entities);
    }
    

一般的なテンプレート変数

テンプレートでは以下の変数が利用できます:

エンティティテンプレート

  • entityDesc.packageName - Package name for the entity

  • entityDesc.simpleName - Simple class name (e.g., "User")

  • entityDesc.tableName - Database table name

  • entityDesc.comment - Table comment from database

  • entityDesc.propertyDescs - List of property descriptors

DAOテンプレート

  • daoDesc.packageName - Package name for the DAO

  • daoDesc.simpleName - Simple interface name (e.g., "UserDao")

  • daoDesc.entityDesc - Associated entity descriptor

  • daoDesc.configClassName - Doma config class name

プロパティ記述子

  • property.propertyName - Java property name (e.g., "userId")

  • property.propertyClassName - Java type (e.g., "Integer")

  • property.columnName - Database column name

  • property.id - True if primary key

  • property.version - True if version column

  • property.comment - Column comment from database

高度なテンプレート機能

条件付き生成

<#-- Only generate if table has a version column -->
<#if entityDesc.versionPropertyDesc??>
@Version
public ${entityDesc.versionPropertyDesc.propertyClassName} ${entityDesc.versionPropertyDesc.propertyName};
</#if>

プロパティに基づくカスタムインポート

<#-- Import specific types based on entity properties -->
<#assign hasTimestamp = false>
<#list entityDesc.propertyDescs as property>
    <#if property.propertyClassName == "java.sql.Timestamp">
        <#assign hasTimestamp = true>
    </#if>
</#list>

<#if hasTimestamp>
import java.sql.Timestamp;
</#if>

トラブルシューティング

よくある問題と解決策

問題:「No suitable driver found」エラー

[DOMAGEN0033] The class "org.postgresql.Driver" to which the parameter "driverClassName" refers is not found.

解決策: domaCodeGen 設定にJDBCドライバーの依存関係を追加したことを確認してください:

dependencies {
    domaCodeGen("org.postgresql:postgresql:42.7.7")
}

問題:生成されたコードが間違ったパッケージにある

解決策: パッケージ設定を確認してください:

entity {
    packageName.set("com.example.entity")  // Ensure this is set correctly
}
dao {
    packageName.set("com.example.dao")     // Ensure this is set correctly
}

問題:カスタムテンプレートが適用されない

解決策: テンプレートディレクトリの構造とファイル名を確認してください:

your-project/
├── template/
│   ├── entity.ftl           # Must match exact filename
│   ├── dao.ftl
│   └── entityListener.ftl
└── build.gradle.kts
domaCodeGen {
    register("mydb") {
        templateDir.set(file("$projectDir/template"))  // Point to template directory
    }
}

ベストプラクティス

  1. 開発にTestcontainersを使用

    Testcontainersは異なるマシン間で一貫したデータベース環境を保証します:

    // Preferred approach
    url.set("jdbc:tc:postgresql:15:///test?TC_INITSCRIPT=file:${initScript.absolutePath}")
    
  2. スキーマファイルにバージョン管理を使用

    初期化スクリプトをバージョン管理下に置いてください:

    src/main/resources/
    ├── schema-postgresql.sql
    ├── schema-mysql.sql
    └── test-data.sql
    
  3. 段階的な生成

    高速な開発のために特定のタスクを使用してください:

    # Generate only entities (faster for schema changes)
    ./gradlew domaCodeGenMydbEntity
    
    # Generate only DAOs (faster for new tables)
    ./gradlew domaCodeGenMydbDao
    

サンプルプロジェクト