はじめに
私が経験した案件において、
異なるデータソース、同一テーブル定義を用いたシステムを構築したので、
そのときのノウハウを共有します。
データソース定義
ドライバは「Oracle」を使用していますが、環境に合わせて適宜修正してください。
application.yml
spring:
datasource:
# データソースA
alpha:
jdbc-url: jdbc:oracle:thin:@localhost:1521/XEPDB1
username: sample_a
password: sample_a
driverClassName: oracle.jdbc.OracleDriver
# データソースB
beta:
jdbc-url: jdbc:oracle:thin:@localhost:1521/XEPDB1
username: sample_b
password: sample_b
driverClassName: oracle.jdbc.OracleDriver
MyBatisの設定
DataSourceConfig.java
package com.example;
@Configuration
@MapperScan(basePackages = { "com.example.mapper" }, sqlSessionTemplateRef = "sqlSessionTemplate") // ①
public class DataSourceConfig {
@Bean(name = "alphaDataSource")
@ConfigurationProperties(prefix = "spring.datasource.alpha")
public DataSource alphaDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "betaDataSource")
@ConfigurationProperties(prefix = "spring.datasource.beta")
public DataSource betaDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("dynamicDataSource")
public DynamicRoutingDataSourceResolver dynamicDataSource(@Qualifier("alphaDataSource") DataSource alphaDataSource, @Qualifier("betaDataSource") DataSource betaDataSource) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
targetDataSources.put(DataSourceType.ALPHA, alphaDataSource);
targetDataSources.put(DataSourceType.BETA, betaDataSource);
// データソースの動的変更
DynamicRoutingDataSourceResolver resolver = new DynamicRoutingDataSourceResolver();
resolver.setTargetDataSources(targetDataSources);
resolver.setDefaultTargetDataSource(alphaDataSource);
return resolver;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicRoutingDataSourceResolver resolver) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// ②
factoryBean.setDataSource(resolver);
factoryBean.setMapperLocations("Mapper XMLのパス");
factoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return factoryBean.getObject();
}
// ... 省略
}
①: 共通使用するMapperクラスのエイリアスを設定する
②: マルチデータソースではAutoConfigureが無効になるため、手動で設定する
※公式サイトに記載があります
データソース切替
参照データソースを判断するための区分値を定義する。
public enum DataSourceType {
ALPHA, BETA
}
スレッド単位で区分値を保持する。
DataSourceContextHolder.java
public class DataSourceContextHolder {
private static ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<DataSourceType>();
public static void setDataSourceType(DataSourceType dataSourceType) {
contextHolder.set(dataSourceType);
}
public static DataSourceType getDataSourceType() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
参照データソース判断用の区分値を取得する。
この区分値でDataSourceConfig.dynamicDataSourceで設定されたデータソースから、
実際に参照するデータソースを判断する。
DynamicRoutingDataSourceResolver.java
public class DynamicRoutingDataSourceResolver extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
使用方法
DataSourceContextHolder経由で、DataSourceTypeを設定しましょう。
横断処理として定義したい場合は、AOPなどを使用してデータソース切替を行ってください。
DataSourceContextHolder.setDataSourceType(DataSourceType.ALPHA);