本稿では、従来の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つの主要な使用形態が判明しました。
- JdbcTemplateの直接利用:データソースを注入し、JdbcTemplateを生成してSQLを実行するパターン
- MyBatis統合:SqlSessionFactoryBean経由でMyBatisと連携する構成
- Spring Security連携:認証・認可情報の取得にデータソースを利用
- 国際化対応:多言語リソースの取得にデータベースを参照
現在のプロジェクトでは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") を呼び出します。
単一データソースから複数データソースへの移行は計画的な実装が求められますが、既存システムの構造を理解し、段階的に改良を進めることで確実な対応が可能です。