S2JDBCで複数データソースを使い分ける
S2JDBCで複数データソースを使い分ける方法はこちらにあった。
今回自分がしたいのは、同じ構成のテーブルでデータソースを分けたいので、dataSourceFactoryを使って分けるようにする。
一応自分がした設定を記述してみる。
設定ファイル
jdbc_datasource1.diconとjdbc_datasource2.diconはこちらにあるような感じで書いた。
で、2つのファイルをインクルードして、SelectableDataSourceProxyとDataSourceFactoryImplを定義した。
s2jdbc.diconもjdbcManagerは1つにするので、変更していない。
jdbc_datasource1.dicon
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> .... <components namespace="jdbc"> <component name="datasource1DataSource" class="org.seasar.extension.dbcp.impl.DataSourceImpl"/> </components>
jdbc_datasource2.dicon
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> .... <components namespace="jdbc"> <component name="datasource2DataSource" class="org.seasar.extension.dbcp.impl.DataSourceImpl"/> </components>
jdbc.dicon
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> <components namespace="jdbc"> <include path="jdbc_datasource1.dicon"/> <include path="jdbc_datasource2.dicon"/> <component name="dataSource" class="org.seasar.extension.datasource.impl.SelectableDataSourceProxy"/> <component name="dataSourceFactory" class="org.seasar.extension.datasource.impl.DataSourceFactoryImpl"/> </components>
インターセプタ
データソース用のアノテーション、enumとインターセプタを作った。
アノテーションで指定されているデータソースenumを見て、データソースを動的に切り替える仕組み。
インターセプタ
public class DataSourceSettingInterceptor extends AbstractInterceptor { /** データソースファクトリ */ @Resource protected DataSourceFactory dataSourceFactory; /** デフォルトデータソース */ private static final DataSourceType DEFAULT_DATA_SOURCE = DataSourceType.DATASOURCE1; /** * {@inheritDoc} */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { DataSourceType currentDataSourceType = findCurrentDataSourceType(); try { DataSourceType dataSourceType = findDataSourceType(invocation.getMethod()); dataSourceFactory.setSelectableDataSourceName(dataSourceType.getDataSourceName()); return invocation.proceed(); } finally { dataSourceFactory.setSelectableDataSourceName(currentDataSourceType.getDataSourceName()); } } /** * 現在のデータソースタイプを取得する。<br> * @return 現在のデータソースタイプ */ private DataSourceType findCurrentDataSourceType() { String currentDataSourceName = dataSourceFactory.getSelectableDataSourceName(); if (StringUtils.isEmpty(currentDataSourceName)) { return DEFAULT_DATA_SOURCE; } DataSourceType dataSourceType; try { dataSourceType = DataSourceType.toDataSourceType(currentDataSourceName); } catch (IllegalArgumentException e) { // 想定外のデータソース名が渡された場合は、デフォルトデータソースを返す。 return DEFAULT_DATA_SOURCE; } return dataSourceType; } /** * データソースタイプを取得する。<br> * @param method メソッド * @return データソースタイプ */ private DataSourceType findDataSourceType(Method method) { DataSource dataSource = method.getAnnotation(DataSource.class); if (dataSource == null) { return DEFAULT_DATA_SOURCE; } return dataSource.type(); } }
アノテーション
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { /** * データソースタイプを設定する。<br> */ DataSourceType type(); }
enum
public enum DataSourceType { /** データソース1用 */ DATASOURCE1("datasource1"), /** データソース2用 */ DATASOURCE2("datasource2"); /** データソース名 */ private String dataSourceName; /** * コンストラクタ<br> * @param newDataSourceName データソース名 */ private DataSourceType(String newDataSourceName) { this.dataSourceName = newDataSourceName; } /** * データソース名を取得する。<br> * @return データソース名 */ public String getDataSourceName() { return this.dataSourceName; } /** * 指定のデータソース名から対応するデータソースタイプを取得する。<br> * @param dataSourceName データソース名 * @return 対応するデータソースタイプ */ public static DataSourceType toDataSourceType(String dataSourceName) { for (DataSourceType dataSourceType : DataSourceType.values()) { if (dataSourceType.getDataSourceName().equals(dataSourceName)) { return dataSourceType; } } throw new IllegalArgumentException("指定のデータソース名に合致するデータソースタイプはありません。"); } }
インターセプタの使い方
一応、使い方。
@DataSource(type = DataSourceType.DATASOURCE1) public String hoge() { // do something }
あと、一部の機能のみデータソースを切り替えて実行したいときのために、エグゼキュータを作ってみた。
エグゼキュータ
public final class SelectableDataSourceExecutor { /** * コンストラクタ<br> */ private SelectableDataSourceExecutor() { } /** * データソースを指定のデータソースタイプに切り替えて実行する。 * @param <RESULT> 戻り値の型 * @param dataSourceType データソースタイプ * @param handler ハンドラ * @return 結果 */ public static <RESULT> RESULT execute(DataSourceType dataSourceType, Handler<RESULT> handler) { DataSourceFactory dataSourceFactory = SingletonS2Container.getComponent(DataSourceFactory.class); String currentDataSourceName = dataSourceFactory.getSelectableDataSourceName(); try { dataSourceFactory.setSelectableDataSourceName(dataSourceType.getDataSourceName()); return handler.handle(); } finally { dataSourceFactory.setSelectableDataSourceName(currentDataSourceName); } } /** * データソースの切り替え時に呼び出されるハンドラ。<br> * @param <RESULT> 結果の型 */ public interface Handler<RESULT> { /** * データソースの切り替え後に実行する。<br> * @return 結果 */ RESULT handle(); } }
エグゼキュータの使い方
一応、使い方。
public void hoge(final HogeDao dao, final HogeEntity entity) // do something SelectableDataSourceExecutor.execute(DataSourceType.DATASOURCE1, new SelectableDataSourceExecutor.Handler<Integer>() { /** * {@inheritDoc} */ @Override public Integer handle() { return dao.insert(entity); } }); // do something }