単一データソースから複数データソースへの移行実践

本稿では、従来のJSP Webアプリケーションを単一データソースから複数データソース対応へ移行する実践的なアプローチについて解説します。対象プロジェクトはWAR形式でデプロイされ、JDK 1.7環境で動作するSpring MVCベースのシステムです(Spring Bootは未使用)。既存の単一データソースアーキテクチャを、複数のデータベースを動的に切り替えられる構成へ拡張する手法を説明します。

データソース利用状況の分析

まずプロジェクト全体のデータソース参照箇所を特定する必要があります。グローバル検索で「datasource」キーワードを抽出し、命名規則が適切でない疑似参照を除外しました。特定された参照箇所は以下の3つのカテゴリに分類できます。

XML設定ファイルの分析:Spring MVCの伝統的な設定では、Beanの依存関係がXMLファイルに明記されています。Bean定義と依存性注入の構造を詳細に分析する必要があります。

Javaコードでの利用:XML設定で定義されたBeanがJavaクラスに注入され、実行時に利用されます。注入されたBeanの具体的な使用箇所を特定します。

プロパティファイルの確認:データベース接続情報など、個別の設定項目がpropertiesファイルに存在するかを検証します。

ビジネスロジックの整理

データソースの利用パターンを分析した結果、以下の4つの主要な使用形態が判明しました。

  1. JdbcTemplateの直接利用:データソースを注入し、JdbcTemplateを生成してSQLを実行するパターン
  2. MyBatis統合:SqlSessionFactoryBean経由でMyBatisと連携する構成
  3. Spring Security連携:認証・認可情報の取得にデータソースを利用
  4. 国際化対応:多言語リソースの取得にデータベースを参照

現在のプロジェクトではJNDI経由でデータソースを注入しているため、複数データソース対応には独自の工夫が必要です。アプローチとして、動的キーによるデータソース切り替え機構の導入を検討しました。

実装方針

複数データソースの定義から始めます。

<bean id="primaryDb" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://db01.example.com:3306/app_db"/>
  <property name="username" value="app_user"/>
  <property name="password" value="secure_pass"/>
</bean>

<bean id="secondaryDb" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://db02.example.com:3306/log_db"/>
  <property name="username" value="log_user"/>
  <property name="password" value="log_pass"/>
</bean>

<bean id="sessionFactory" class="DynamicSqlSessionFactoryBean">
    <property name="multipleDataSources">
        <map key-type="java.lang.String">
            <entry key="primary" value-ref="primaryDb"/>
            <entry key="secondary" value-ref="secondaryDb"/>
        </map>
    </property>
</bean>

データソースコンテキスト管理クラスを実装します。

public class DatabaseContext {
    private static final ThreadLocal<String> storage = new ThreadLocal<>();

    public static void switchDatabase(String identifier) {
        storage.set(identifier);
    }

    public static String getCurrentDatabase() {
        return storage.get();
    }

    public static void resetDatabase() {
        storage.remove();
    }
}

動的データソースルーティングを実装します。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class RouteableDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DatabaseContext.getCurrentDatabase();
    }
}

SqlSessionFactoryBeanを拡張して動的データソースを統合します。

public class DynamicSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
    private RouteableDataSource switchableDataSource;

    public void setMultipleDataSources(Map<Object, Object> dataSourceMap) {
        switchableDataSource = new RouteableDataSource();
        switchableDataSource.setTargetDataSources(dataSourceMap);
        switchableDataSource.setDefaultTargetDataSource(dataSourceMap.values().iterator().next());
        super.setDataSource(switchableDataSource);
    }

    @Override
    protected SqlSessionFactory createSqlSessionFactory() throws IOException {
        return super.buildSqlSessionFactory();
    }

    @Override
    public void destroy() {
        // リソース解放処理
    }
}

データソース切り替えが必要な箇所で、DatabaseContext.switchDatabase("primary") または DatabaseContext.switchDatabase("secondary") を呼び出します。

単一データソースから複数データソースへの移行は計画的な実装が求められますが、既存システムの構造を理解し、段階的に改良を進めることで確実な対応が可能です。

タグ: Spring MVC MyBatis データソース マイグレーション

6月13日 17:51 投稿