返回 Skill 列表
extension
分类: 开发与工程无需 API Key

database-adapter

在开发和修改数据库适配器(同时支持PostgreSQL和MySQL)时使用。在实现SqlExecutor模式、迁移以及特定于数据库的SQL实现时非常有用。

person作者: jakexiaohubgithub

データベースアダプター開発ガイド

ドキュメント

  • libs/idp-server-database/README.md - データベース設計・マイグレーション
  • documentation/docs/content_10_ai_developer/ai-20-adapters.md - アダプター実装ガイド
  • documentation/docs/content_10_ai_developer/ai-21-core-adapter.md - Core Adapterガイド
  • documentation/docs/content_10_ai_developer/ai-22-database.md - データベースガイド

機能概要

データベースアダプターは、PostgreSQL/MySQL両方をサポートする永続化層。

  • 両DB対応: 全機能でPostgreSQL/MySQL両方実装必須
  • SqlExecutorパターン: DB固有SQLを抽象化
  • Flyway: マイグレーション管理
  • 読み書き分離: @Transaction / @Transaction(readOnly = true) アノテーション

モジュール構成

libs/
├── idp-server-core-adapter/                    # 永続化実装
│   └── .../adapters/datasource/
│       ├── audit/
│       │   ├── command/
│       │   │   ├── AuditLogSqlExecutor.java       # インターフェース
│       │   │   ├── AuditLogSqlExecutors.java      # ファクトリ
│       │   │   ├── PostgresqlExecutor.java        # PostgreSQL実装
│       │   │   └── MysqlExecutor.java             # MySQL実装
│       │   └── query/
│       │       └── (同様の構造)
│       ├── token/
│       ├── identity/
│       ├── authentication/
│       ├── ciba/
│       └── ...
│
├── idp-server-database/                        # マイグレーション
│   ├── postgresql/
│   │   ├── V0_9_0__init_lib.sql               # 初期スキーマ
│   │   └── V1_0_0__*.sql                      # バージョン別マイグレーション
│   └── mysql/
│       ├── V0_9_0__init_lib.mysql.sql         # ※ .mysql.sql 接尾辞
│       └── V0_9_21_1__*.mysql.sql
│
└── idp-server-platform/                        # 共通基盤
    └── .../datasource/
        ├── DatabaseType.java                   # POSTGRESQL, SPANNER, MYSQL
        ├── SqlExecutor.java                    # SQL実行ヘルパー
        └── TransactionManager.java             # トランザクション管理

SqlExecutor パターン

基本構造

AuditLogSqlExecutor.java:

// 1. インターフェース定義(Tenant第一引数)
public interface AuditLogSqlExecutor {
    void insert(Tenant tenant, AuditLog auditLog);
}

PostgresqlExecutor.java:

// 2. PostgreSQL実装
public class PostgresqlExecutor implements AuditLogSqlExecutor {
    @Override
    public void insert(Tenant tenant, AuditLog auditLog) {
        // SqlExecutorが内部でTransactionManager.getConnection()を使用
        SqlExecutor sqlExecutor = new SqlExecutor();
        String sqlTemplate = """
            INSERT INTO audit_log (
                id, type, description, tenant_id, client_id, user_id, ...
            ) VALUES (
                ?::uuid, ?, ?, ?::uuid, ...
            );
            """;
        List<Object> params = new ArrayList<>();
        params.add(auditLog.id().value());
        // ...
        sqlExecutor.execute(sqlTemplate, params);
    }
}

MysqlExecutor.java:

// 3. MySQL実装
public class MysqlExecutor implements AuditLogSqlExecutor {
    @Override
    public void insert(Tenant tenant, AuditLog auditLog) {
        SqlExecutor sqlExecutor = new SqlExecutor();
        String sqlTemplate = """
            INSERT INTO audit_log (
                id, type, description, tenant_id, client_id, user_id, ...
            ) VALUES (
                ?, ?, ?, ?, ...
            );
            """;
        // MySQL固有: UUIDキャストなし
        // ...
    }
}

AuditLogSqlExecutors.java:

// 4. ファクトリ(ディスパッチャー)
public class AuditLogSqlExecutors {
    Map<DatabaseType, AuditLogSqlExecutor> executors;

    public AuditLogSqlExecutors() {
        executors = new HashMap<>();
        executors.put(DatabaseType.POSTGRESQL, new PostgresqlExecutor());
        executors.put(DatabaseType.MYSQL, new MysqlExecutor());
    }

    public AuditLogSqlExecutor get(DatabaseType databaseType) {
        return executors.get(databaseType);
    }
}

読み書き分離

EntryServiceレイヤーで @Transaction アノテーションにより制御:

// 書き込み(Primary)
@Transaction
public class OAuthFlowEntryService { ... }

// 読み取り専用(Replica)
@Transaction(readOnly = true)
public class UserinfoEntryService { ... }

@Transaction(readOnly = true)
public class OidcMetaDataEntryService { ... }

PostgreSQL vs MySQL 差異

INSERT時の競合処理

| 操作 | PostgreSQL | MySQL | |------|-----------|-------| | UPSERT | ON CONFLICT (id) DO UPDATE SET ... | ON DUPLICATE KEY UPDATE ... | | INSERT IGNORE | ON CONFLICT DO NOTHING | INSERT IGNORE INTO ... |

UUID型

| 操作 | PostgreSQL | MySQL | |------|-----------|-------| | UUIDキャスト | ?::uuid | ? (文字列として保存) |

JSON操作

| 操作 | PostgreSQL | MySQL | |------|-----------|-------| | JSON型 | jsonb | JSON | | JSON抽出 | column->>'key' | JSON_UNQUOTE(JSON_EXTRACT(column, '$.key')) | | JSON検索 | column @> '{"key": "value"}' | JSON_CONTAINS(column, '"value"', '$.key') |

日時操作

| 操作 | PostgreSQL | MySQL | |------|-----------|-------| | 現在時刻 | NOW() | NOW() | | タイムゾーン | AT TIME ZONE 'UTC' | CONVERT_TZ(..., '+00:00', 'UTC') | | 間隔 | INTERVAL '1 day' | INTERVAL 1 DAY |


マイグレーション

ファイル命名規則

PostgreSQL: V{major}_{minor}_{patch}__{description}.sql
MySQL:      V{major}_{minor}_{patch}__{description}.mysql.sql

例:
- postgresql/V1_2_0__add_user_status_column.sql
- mysql/V1_2_0__add_user_status_column.mysql.sql

注意: MySQLは .mysql.sql 接尾辞が必須。

実行方法

# Docker経由(推奨)
docker compose up flyway-migrator

# 直接実行
./gradlew :libs:idp-server-database:flywayMigrate

両DB対応マイグレーション

PostgreSQL と MySQL で同じバージョン番号のファイルを作成:

libs/idp-server-database/
├── postgresql/
│   └── V1_2_0__add_user_status_column.sql
└── mysql/
    └── V1_2_0__add_user_status_column.mysql.sql

Command / Query 分離

構造パターン

datasource/{domain}/
├── command/                    # 書き込み操作
│   ├── {Domain}CommandSqlExecutor.java
│   ├── {Domain}CommandSqlExecutors.java
│   ├── PostgresqlExecutor.java
│   └── MysqlExecutor.java
└── query/                      # 読み取り操作
    ├── {Domain}QuerySqlExecutor.java
    ├── {Domain}QuerySqlExecutors.java
    ├── PostgresqlExecutor.java
    └── MysqlExecutor.java

DatabaseType

DatabaseType.java:

public enum DatabaseType {
    POSTGRESQL,
    SPANNER,     // Google Cloud Spanner
    MYSQL;
}

コマンド

# ビルド
./gradlew :libs:idp-server-core-adapter:compileJava
./gradlew :libs:idp-server-database:build

# マイグレーション確認
./gradlew :libs:idp-server-database:flywayInfo

# テスト
./gradlew :libs:idp-server-core-adapter:test

チェックリスト

新規DataSource実装時:

  • [ ] SqlExecutor インターフェース作成(Tenant第一引数)
  • [ ] PostgresqlExecutor 実装
  • [ ] MysqlExecutor 実装
  • [ ] SqlExecutors ファクトリ作成
  • [ ] DataSource クラス作成
  • [ ] PostgreSQL マイグレーション追加(.sql)
  • [ ] MySQL マイグレーション追加(.mysql.sql)
  • [ ] 両DBでテスト実行

トラブルシューティング

SQL構文エラー

| 問題 | 原因 | 解決策 | |------|------|--------| | PostgreSQLで動くがMySQLでエラー | DB固有構文の混在 | 両Executor実装を確認 | | UUIDキャストエラー | ?::uuidがMySQLで失敗 | MySQL版では文字列として扱う | | JSONクエリ失敗 | JSON関数の差異 | 上記JSON操作表を参照 |

マイグレーション失敗

| 問題 | 原因 | 解決策 | |------|------|--------| | MySQLでファイル認識されない | 接尾辞が.sql | .mysql.sqlに変更 | | バージョン競合 | 既存マイグレーションとの衝突 | flyway repair 実行 | | スキーマ不整合 | 手動変更との不一致 | docker compose down -v で初期化 |


関連スキル

| スキル | 用途 | |--------|------| | /local-environment | ローカルDB環境構築 | | /architecture | アーキテクチャ全体像 | | /operations | 運用・監視 |