Domaアダプタ

Doma2(外部サイト) を使用したデータベースアクセスを行うためのアダプタを提供する。

データベースアクセスにDomaを使用することで以下のメリットが得られる。

  • Nablarchと同じように、実行時に動的にSQL文を構築することができる。
  • 2waySQLなので、NablarchのようにSQL文を書き換える必要がなく、SQLツール等でそのまま実行することができる。

また、本アダプタを使用することで、 Transactional インターセプタで 指定したアクションのみトランザクション管理対象とすることができるため、 不要なトランザクション制御処理を削減でき、パフォーマンスの向上が期待できる。

モジュール一覧

<!-- Domaアダプタ -->
<dependency>
  <groupId>com.nablarch.integration</groupId>
  <artifactId>nablarch-doma-adaptor</artifactId>
</dependency>

ちなみに

Domaのバージョン2.16.0を使用してテストを行っている。 バージョンを変更する場合は、プロジェクト側でテストを行い問題ないことを確認すること。

Domaアダプタを使用するための設定を行う

本アダプタを使用するためには、プロジェクトで使用するRDBMSに合わせてDomaのダイアレクトやデータソースをコンポーネント定義ファイルに設定する必要がある。

H2を使用する場合の設定例を以下に示す。

ポイント
  • 定義するダイアレクトは org.seasar.doma.jdbc.dialect.Dialect の実装クラスとすること
  • ダイアレクトのコンポーネント名は domaDialect とすること
  • データソースのコンポーネント名は dataSource とすること
<component name="domaDialect" class="org.seasar.doma.jdbc.dialect.H2Dialect"  />
<component name="dataSource" class="org.h2.jdbcx.JdbcDataSource">
  <!-- プロパティは省略 -->
</component>

Domaを使用してデータベースにアクセスする

Domaを使用したデータベースアクセスを行うための手順を以下に示す。

Daoインタフェースを作成する

データベースアクセスを行うためのDao(Data Access Object)インタフェースを作成する。

ポイント
  • Daoアノテーションのconfig属性には DomaConfig を指定する
@Dao(config = DomaConfig.class)
public interface ProjectDao {
    // 省略
}

データベースアクセス処理を実装する

業務アクションのメソッドにデータベースアクセス処理を実装する。

ポイント
  • 業務アクションメソッドをトランザクション管理対象とするため、 Transactional インターセプタを設定する
  • DomaDaoRepository#get を使用してDaoの実装クラスをルックアップする

ちなみに

Domaでは注釈処理によってコンパイル時に自動的にDaoの実装クラスが生成されるため、コーディング時にはまだ実装クラスが存在しない。 そのため、本アダプタではDaoの実装クラスをルックアップする機能として DomaDaoRepository を提供している。

@Transactional
public HttpResponse create(final HttpRequest request, final ExecutionContext context) {
    final Project project = SessionUtil.delete(context, "project");

    DomaDaoRepository.get(ProjectDao.class).insert(project);

    return new HttpResponse("redirect://complete");
}

別トランザクションで実行する

Transactional インターセプタによって開始されたトランザクションではなく、 別のトランザクションを使用してデータベースアクセスを行いたい場合がある。

その場合は、 DomaConfig#getTransactionManager で取得した TransactionManager を使用して別トランザクションでの制御を行う。

実装例を以下に示す。

DomaConfig.singleton()
        .getTransactionManager()
        .requiresNew(() ->
                DomaDaoRepository.get(ProjectDao.class).insert(project);

JSR352に準拠したバッチアプリケーションで使用する

JSR352に準拠したバッチアプリケーションでDomaを使用するために、 本アダプタでは以下のリスナーを提供している。

これらのリスナーをリスナーリストに定義することで、 JSR352に準拠したバッチアプリケーションでもDomaを使用したデータベースアクセスを行うことができる。

設定例を以下に示す。

<list name="stepListeners">
  <!-- その他のリスナーは省略 -->
  <component class="nablarch.integration.doma.batch.ee.listener.DomaTransactionStepListener" />
</list>

<list name="itemWriteListeners">
  <!-- その他のリスナーは省略 -->
  <component class="nablarch.integration.doma.batch.ee.listener.DomaTransactionItemWriteListener" />
</list>

重要

Chunkステップ のItemWriterでデータベースに対するバッチ更新(バッチinsertやバッチupdateなど)を行う場合、バッチサイズの指定を明示的に行う必要がある。 ※Chunkステップのitem-countのサイズがバッチサイズとなるわけではないので注意すること

これを行わなかった場合、Domaのデフォルト値が適用されるため、バッチ更新を使用してもパフォーマンスが向上しない可能性がある。

実装例

例えば、1000件ごとにバッチinsertを行う場合には、Daoのメソッドを以下のように実装する。

@BatchInsert(batchSize = 1000)
int[] batchInsert(List<Bonus> bonuses);

JSR352に準拠したバッチアプリケーションで遅延ロードを行う

JSR352に準拠したバッチアプリケーションで大量データの読み込みを行う際に、遅延ロードを使用したい場合がある。

その場合は、Daoアノテーションのconfig属性に DomaTransactionNotSupportedConfig を指定する。

重要

config属性に DomaConfig を使用すると、 DomaTransactionItemWriteListener によるトランザクションのコミットでストリームがクローズされるため、後続のレコードが読み込めなくなってしまう。

実装例を以下に示す。

Daoインタフェース
ポイント
@Dao(config = DomaTransactionNotSupportedConfig.class)
public interface ProjectDao {

    @Select(strategy = SelectType.RETURN)
    Stream<Project> search();
}
ItemReaderクラス
ポイント
  • openメソッドで検索結果のストリームを取得する。
  • リソースの解放漏れを防ぐため、closeメソッドで必ずストリームを閉じる。
@Dependent
@Named
public class ProjectReader extends AbstractItemReader {

    private Iterator<Project> iterator;

    private Stream<Project> stream;

    @Override
    public void open(Serializable checkpoint) throws Exception {
        final ProjectDao dao = DomaDaoRepository.get(ProjectDao.class);
        stream = dao.search();
        iterator = stream.iterator();
    }

    @Override
    public Object readItem() {
        if (iterator.hasNext()) {
            return iterator.next();
        } else {
            return null;
        }
    }

    @Override
    public void close() throws Exception {
        stream.close();
    }
}

ETLで使用する

ETL使用時に、プロジェクトで追加したステップの中でDomaを使用したい場合がある。 その場合は、ジョブ名およびステップ名を指定したリスナーリストを定義して対応する。

設定例を以下に示す。

ジョブ定義ファイル
<job id="sampleJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="sampleStep">
    <listeners>
      <listener ref="nablarchStepListenerExecutor" />
      <listener ref="nablarchItemWriteListenerExecutor" />
    </listeners>
    <chunk>
      <reader ref="sampleItemReader" />
      <writer ref="sampleItemWriter" />
    </chunk>
  </step>
</job>
コンポーネント定義ファイル
<list name="sampleJob.sampleStep.stepListeners">
  <!-- その他のリスナーは省略 -->
  <component
      class="nablarch.integration.doma.batch.ee.listener.DomaTransactionStepListener" />
</list>

<list name="sampleJob.sampleStep.itemWriteListeners">
  <!-- その他のリスナーは省略 -->
  <component
      class="nablarch.integration.doma.batch.ee.listener.DomaTransactionItemWriteListener" />
</list>

複数のデータベースにアクセスする

複数のデータベースにアクセスする必要がある場合は、新しくConfigクラスを作成し、 別のデータベースへのアクセスはそのConfigクラスを使用して行うように実装する。

実装例を以下に示す。

コンポーネント定義ファイル
<component name="customDomaDialect" class="org.seasar.doma.jdbc.dialect.OracleDialect"  />
<component name="customDataSource" class="oracle.jdbc.pool.OracleDataSource">
  <!-- プロパティは省略 -->
</component>
Configクラス
@SingletonConfig
public final class CustomConfig implements Config {

    private CustomConfig() {
        dialect = SystemRepository.get("customDomaDialect");
        localTransactionDataSource =
                new LocalTransactionDataSource(SystemRepository.get("customDataSource"));
        localTransaction = localTransactionDataSource.getLocalTransaction(getJdbcLogger());
        localTransactionManager = new LocalTransactionManager(localTransaction);
    }

    // その他のフィールド、メソッドはDomaConfigを参考に実装すること
}
Daoインタフェース
@Dao(config = CustomConfig.class)
public interface ProjectDao {
    // 省略
}
業務アクションクラス
public HttpResponse create(final HttpRequest request, final ExecutionContext context) {
    final Project project = SessionUtil.delete(context, "project");

    CustomConfig.singleton()
            .getTransactionManager()
            .requiresNew(() ->
                    DomaDaoRepository.get(ProjectDao.class).insert(project);

    return new HttpResponse("redirect://complete");
}