AgentSkillsCN

database-adapter

在开发或优化数据库适配器(支持 PostgreSQL 与 MySQL)时,此技能将助您高效运用 SqlExecutor 模式、完成数据库迁移,并灵活实现各类特定于数据库的 SQL 逻辑。

SKILL.md
--- frontmatter
name: database-adapter
description: データベースアダプター(PostgreSQL/MySQL両対応)の開発・修正を行う際に使用。SqlExecutorパターン、マイグレーション、DB固有SQL実装時に役立つ。

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

ドキュメント

  • 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) アノテーション

モジュール構成

code
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:

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

PostgresqlExecutor.java:

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:

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:

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 アノテーションにより制御:

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

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

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

PostgreSQL vs MySQL 差異

INSERT時の競合処理

操作PostgreSQLMySQL
UPSERTON CONFLICT (id) DO UPDATE SET ...ON DUPLICATE KEY UPDATE ...
INSERT IGNOREON CONFLICT DO NOTHINGINSERT IGNORE INTO ...

UUID型

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

JSON操作

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

日時操作

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

マイグレーション

ファイル命名規則

code
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 接尾辞が必須。

実行方法

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

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

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

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

code
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 分離

構造パターン

code
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:

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

コマンド

bash
# ビルド
./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運用・監視