Springで複数DBに接続する方法
かなーり久しぶりのエントリー。
仕事でSpringを使っているのですが、複数のDBに接続する際にちょっとはまってしまったのでメモ。
主にやることは以下の通り。
- application.ymlに接続先DB情報を記載
- それぞれのDBに対してConfigurationを作成
- AbstractRoutingDataSourceのサブクラスを作成
- HandlerInterceptorAdapterのサブクラスで、URLによってスキーマタイプを設定
- Configurationを読み込むクラスを作成
application.ymlに接続先DB情報を記載
application.ymlへ以下のように接続先DB情報を記載する
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/primary
username: root
password:
driverClassName : com.mysql.jdbc.Driver
secondary:
url: jdbc:mysql://localhost:3306/secondary
username: root
password:
driverClassName : com.mysql.jdbc.Driver
それぞれのDBに対してConfigurationを作成
@Data
@Component
@ConfigurationProperties(prefix="spring.datasource.primary")
public class PrimaryConfiguration {
private String driverClassName;
private String url;
private String username;
private String password;
@Bean
public DataSource createDataSource() {
return DataSourceBuilder
.create()
.driverClassName(driverClassName)
.url(url)
.username(username)
.password(password)
.build();
}
}
secondaryも同じように作成します。
AbstractRoutingDataSourceのサブクラスを作成
まずはThreadLocalでスキーマタイプを保持するクラスを作成する
public class SchemaContextHolder {
private static ThreadLocal contextHolder = new ThreadLocal();
public static void setSchemaType(String type) {
contextHolder.set(type);
}
public static String getSchemaType() {
if (contextHolder.get() != null) {
return contextHolder.get();
}
return "Primary";
}
public static void clear() {
contextHolder.remove();
}
}
その上で、AbstractRoutingDataSourceのサブクラスを作成
public class RoutingDataSourceResolver extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// TODO 自動生成されたメソッド・スタブ
if (SchemaContextHolder.getSchemaType() == null) {
return "Primary";
}
if (SchemaContextHolder.getSchemaType().matches("Secondary")) {
return "Secondary";
}
return "Primary";
}
}
HandlerInterceptorAdapterのサブクラスで、URLによってスキーマタイプを設定
HandlerInterceptorAdapterのサブクラスで、スキーマタイプを切り替える処理を入れる。
public class Interceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String url = request.getRequestURI();
if (url.contains("/secondary/")) {
SchemaContextHolder.setSchemaType("Secondary");
} else {
SchemaContextHolder.setSchemaType("Primary");
}
return true;
}
}
Configurationを読み込むクラスを作成
Configurationを読み込むクラスを作成する。
@Component
@Configuration
public class DataSourceConfig {
@Autowired
private PrimaryConfiguration primaryConfiguration;
@Autowired
private SecondConfiguration secondConfiguration;
@Bean
@Primary
public RoutingDataSourceResolver getRoutingdataSource() {
RoutingDataSourceResolver resolver = new RoutingDataSourceResolver();
Map<Object, Object> dataSources = new HashMap<Object, Object>();
dataSources.put("Primary", primaryConfiguration.createDataSource());
dataSources.put("Secondary", secondConfiguration.createDataSource());
resolver.setTargetDataSources(dataSources);
// default datasource
resolver.setDefaultTargetDataSource(primaryConfiguration.createDataSource());
return resolver;
}
@Bean
public HandlerInterceptor datasourceInterceptor() {
// Interceptorの設定
return new com.kajishima.common.Interceptor();
}
@Bean
public MappedInterceptor interceptorMapping() {
return new MappedInterceptor(new String[] { "/**" }, datasourceInterceptor());
}
}
コードはこちら