ありまっくすのへっぽこ日記

アラフォーでへっぽこエンジニアをしております。へっぽこなりに頑張っています。

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());
	}
}

コードはこちら

github.com