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>を使っても実現できる気がする。

ソースは ここ に置いてます。

 

参考にしたページ