Spring Boot + Domaで動的データソース切り替えを実現する
Spring Boot + Domaでjsonを返すRESTサーバを作ってみました。
マルチテナントアプリという要件があったので、データソースを動的に切り替えられるようにしています。
以前試した CDIで複数データソースの切り替え のSpring + Doma版です。
データソースを動的に切り替えるためのポイントは以下になります。
@ComponentScanアノテーションのscopeResolverでJsr330ScopeMetadataResolverを指定し、スコープをPrototypeにする
@EnableAutoConfiguration
@ComponentScan(basePackages = "info.matsumana", scopeResolver = Jsr330ScopeMetadataResolver.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AbstractRoutingDataSourceを継承したクラスを作り(サンプルソースではDynamicRoutingDataSourceResolver)、
determineCurrentLookupKeyメソッドでデータソース名をreturnする
このサンプルソースではシステム時刻でデータソースを切り替えていますが、実際はHTTPヘッダーやCookieで切り替える事になると思います
public class DynamicRoutingDataSourceResolver extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// システム時刻が偶数秒ならdataSource1に接続する
DateTime dt = new DateTime();
int sec = Integer.parseInt(dt.toString("s"));
if (sec % 2 == 0) {
return "dataSource1";
} else {
return "dataSource2";
}
}
}
AppConfigにデータソースを複数定義しておき(サンプルソースではdataSource1とdataSource2)、
DynamicRoutingDataSourceResolverでラップしてreturnするメソッドをDataSouceとして使用する
@Configuration
public class AppConfig {
@Bean()
public DataSource dataSource1() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/db1");
ds.setUsername("postgres");
ds.setDefaultAutoCommit(false);
return ds;
}
@Bean()
public DataSource dataSource2() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUrl("jdbc:postgresql://localhost:5432/db2");
ds.setUsername("postgres");
ds.setDefaultAutoCommit(false);
return ds;
}
@Bean()
public DynamicRoutingDataSourceResolver dataSource() {
DynamicRoutingDataSourceResolver resolver = new DynamicRoutingDataSourceResolver();
Map<Object, Object> dataSources = Maps.newHashMap();
dataSources.put("dataSource1", dataSource1());
dataSources.put("dataSource2", dataSource2());
resolver.setTargetDataSources(dataSources);
return resolver;
}
}
これで各コンポーネントが毎回インスタンス化され、
DynamicRoutingDataSourceResolver#determineCurrentLookupKey()が毎回呼ばれるようになります。
Provider<T>
を使っても実現できる気がする。
ソースは ここ に置いてます。
参考にしたページ