SeasarプロジェクトSeasar.NETプロジェクトS2Container.NETQuillで簡単DI+AOP > Quillで複数データソース接続

複数データソースへの接続


使用するデータソースの設定を記述します。

下記のように設定した場合、二つのデータソースが
それぞれ「FrontDB」「MasterDB」というキーで
QuillContainerインスタンス生成時にQuillに登録されます。

設定ファイルの書き方についてはこちらをご覧下さい。

<quill>
  (中略)
  <dataSources>
    <dataSource name="FrontDB">
      <provider>SqlServer</provider>
      <connectionString>"Server=localhost;database=front;"</connectionString>
      <class>Seasar.Extension.Tx.Impl.TxDataSource</class>
    </dataSource>
  </dataSources>
  <dataSources>
    <dataSource name="MasterDB">
      <provider>SqlServer</provider>
      <connectionString>"Server=localhost;database=master;"</connectionString>
      <class>Seasar.Extension.Tx.Impl.TxDataSource</class>
    </dataSource>
  </dataSources>
</quill>

使用するデータソースの分だけDao設定クラスを用意します。

(Dao設定クラスの作成方法についてはこちらをご覧下さい。)

Dao設定クラスのプロパティ「DataSourceName」が設定ファイルに書いた
データソース名を返すようにします。

/// <summary>
/// 「FrontDB」に接続するDao設定
/// </summary>
public class FrontDaoSetting : TypicalDaoSetting {
  public override string DataSourceName {
    get { return "FrontDB"; }
  }
}

/// <summary>
/// 「MasterDB」に接続するDao設定
/// </summary>
public class MasterDaoSetting : TypicalDaoSetting {
  public override string DataSourceName {
    get { return "MasterDB"; }
  }
}

S2Dao属性の引数に上記で作成したDao設定クラスの型情報を渡します。

(S2Dao属性の使い方についてはこちらをご覧下さい。)

/// <summary>
/// 「FrontDB」のHogeテーブルに接続するDao
/// </summary>
[Implementation]
[Bean(typeof(HogeEntity))]
[S2Dao(typeof(FrontDaoSetting))]
public interface IFrontHogeDao  {
  HogeEntity Select(int? hogeId);
  int Update(HogeEntity entity);
}

/// <summary>
/// 「MasterDB」のItemテーブルに接続するDao
/// </summary>
[Implementation]
[Bean(typeof(ItemEntity))]
[S2Dao(typeof(MasterDaoSetting))]
public interface IMasterItemDao  {
  ItemEntity Select(int? itemId);
  int Update(ItemEntity entity);
}

S2Dao属性、Dao設定クラスと同様に
使用するデータソースの分だけトランザクション設定クラスを用意します。

(トランザクション設定クラスの作成方法についてはこちらをご覧下さい。)

トランザクション設定クラスのプロパティ「DataSourceName」が設定ファイルに書いた
データソース名を返すようにします。

/// <summary>
/// 「FrontDB」に接続するトランザクション設定
/// </summary>
public class FrontTxSetting : TypicalTransactionSetting {
  public override string DataSourceName {
    get { return "FrontDB"; }
  }
}

/// <summary>
/// 「MasterDB」に接続するトランザクション設定
/// </summary>
public class MasterTxSetting : TypicalTransactionSetting {
  public override string DataSourceName {
    get { return "MasterDB"; }
  }
}

Transaction属性の引数に上記で作成したトランザクション設定クラスの型情報を渡します。

(Transaction属性の使い方についてはこちらをご覧下さい。)

[Implementation]
public class HogeFacade  {
  protected IFrontHogeDao _frontHogeDao;
  protected IMasterItemDao _masterItemDao;
  
  /// <summary>
  /// 「FrontDB」にトランザクションをかける
  /// </summary>
  [Transaction(typeof(FrontTxSetting))]
  public int ExecuteUpdateHoge(HogeEntity entity) {
    (中略)
    return _frontHogeDao.Update(entity);
  }

  /// <summary>
  /// 「MasterDB」にトランザクションをかける
  /// </summary>
  [Transaction(typeof(MasterTxSetting))]
  public int ExecuteUpdateItem(ItemEntity entity) {
    (中略)
    return _masterItemDao.Update(entity);
  }
}

下記例のようにDaoの接続先とトランザクションをかける先がずれている場合、
Transaction属性に渡した設定のデータソースに対してトランザクション制御を行います。
Daoの接続先に対してはトランザクション制御は行われません。

[Implementation]
public class HogeFacade  {
  /// <summary>
  /// 「FrontDB」に接続するDao
  /// </summary>
  protected IFrontHogeDao _frontHogeDao;
  
  /// <summary>
  /// 「MasterDB」にトランザクションをかける
  /// </summary>
  [Transaction(typeof(MasterTxSetting))]
  public int ExecuteUpdateItem(HogeEntity entity) {
    (中略)
    return _frontHogeDao.Update(entity);
  }
}

○DBFlute.NET(dfnet-multipledb-quill-example)

DBFlute.NETでは複数データソース接続で使用する設定クラスも
自動生成します。

また、サンプルプロジェクト「dfnet-multipledb-quill-example」では
DBFlute.NET+Quill+複数データソース接続でのサンプルコードが実装されています。

dfnet-multiple-quill-exampleのリポジトリはこちら


○例外発生時のロールバックについて

下記のコードはQuillデフォルトのトランザクション制御処理です。
(Seasar.Extension.Tx.Impl.LocalRequiredTxHandler#HandleTransactionを使用)

try {
  object obj = invocation.Proceed();
  // ※currentはトランザクションを制御するオブジェクト
  current.Commit();
  return obj;
} catch {
  current.Rollback();
  throw;
} finally {
  (略)
}

invocation.Proceedが呼ばれるとTransaction属性を設定したメソッドの中身が
実行されます。
例外が発生した場合はcurrent.Rollback()が呼ばれ、ロールバックが行われます。
その後にはthrowが呼ばれ、呼び出し元に例外を投げ直しています。

複数データソース接続の場合も同じ仕組みで動いているため、

[Transaction(typeof(FrontTxSetting))]
[Transaction(typeof(MasterTxSetting))]
public void Update(HogeEntity hoge, ItemEntity item) {
  _frontHogeDao.Update(hoge);
  _masterItemDao.Update(item);
}

※Seasar.NET 1.3.17~での使用を想定しています。

上記のようなコードの「_masterItemDao.Update(item);」で例外が発生した場合

例外発生

MasterTxSettingで設定されたLocalRequiredTxSetting#HandleTransactionで例外catch

MasterDBに対してロールバック。

例外を再throw

FrontTxSettingで設定されたLocalRequiredTxSetting#HandleTransactionで例外catch

FrontDBに対してロールバック。

例外を再throw

という処理が行われ、FrontDB、MasterDBの両方に対してロールバックが行われます。
そのため、例外を途中でcatchし、再throwはしていない処理が
途中にある場合、ロールバックが行われない可能性があります。
ご注意下さい。



○関連クラスとその役割

複数データソース接続では下記のクラスが主に関わっています。

クラス名役割名前空間
DataSourceSelectInterceptor使用するデータソースの切替。
Dao設定クラス
トランザクション設定クラスで
「DataSourceName」プロパティが
データソース名を返すように
拡張されているとこのインターセプターが
適用される。
Seasar.Quill.Dao.Interceptor
SelectableDataSourceProxy
WithDictionary
Quill内でのデータソース集合クラス。
「SetDataSourceName」を呼び出すと
引数に渡したキー
(設定ファイルのデータソース名)に
該当するデータソースに
接続するようになる。
Seasar.Quill.Database.
DataSource.Impl
TransactionContextトランザクション制御Seasar.Extension.Tx.Impl
LocalRequiredTxHandlerQuill標準のトランザクションハンドラSeasar.Extension.Tx.Impl
TxDataSourceトランザクション用データソースSeasar.Extension.Tx.Impl