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

DomaとNablarchのデータベースアクセスを併用する

データベースアクセスにDomaを採用した場合でも、 Nablarch提供のデータベースアクセス を使用したい場合がある。 例えば、 メール送信ライブラリ を使用する場合が該当する。(メール送信要求データベースアクセス(JDBCラッパー) を使用している。)

この問題を解決するため、Nablarchのデータベースアクセス処理が、Domaと同じトランザクション(データベース接続)を利用できる機能を提供している。

利用手順

コンポーネント設定ファイルに以下の定義を追加する。 これにより、Nablarchのデータベースアクセスが、自動的にDomaのトランザクション配下で実行されるようにある。

  • コンポーネント設定ファイルに ConnectionFactoryFromDomaConnection を定義する。 コンポーネント名は、 connectionFactoryFromDoma とする。
  • JSR352用のDomaのトランザクションを制御するリスナーに、ConnectionFactoryFromDomaConnectionを設定する。
<!-- コンポーネント名は、connectionFactoryFromDomaとする -->
<component name="connectionFactoryFromDoma"
    class="nablarch.integration.doma.ConnectionFactoryFromDomaConnection">

  <!-- プロパティに対する設定は省略 -->

</component>

<!--
JSR352に準拠したバッチアプリケーションで利用する場合は、Domaのトランザクションを制御するリスナーに
上記で定義したconnectionFactoryFromDomaを設定する。
 -->
<component class="nablarch.integration.doma.batch.ee.listener.DomaTransactionItemWriteListener">
  <property name="connectionFactory" ref="connectionFactoryFromDoma" />
</component>

<component class="nablarch.integration.doma.batch.ee.listener.DomaTransactionStepListener">
  <property name="connectionFactory" ref="connectionFactoryFromDoma" />
</component>

ロガーを切り替える

本アダプタではDomaが使うロガーの実装として、Nablarchのロガーを利用する NablarchJdbcLogger を提供している。 デフォルトでは NablarchJdbcLogger が使用されるが、他のものに差し替える場合はコンポーネント定義ファイルに設定する必要がある。

org.seasar.doma.jdbc.UtilLoggingJdbcLogger を使用する場合の設定例を以下に示す。

ポイント
  • 定義するロガーは org.seasar.doma.jdbc.JdbcLogger の実装クラスとすること
  • ロガーのコンポーネント名は domaJdbcLogger とすること
<component name="domaJdbcLogger" class="org.seasar.doma.jdbc.UtilLoggingJdbcLogger"  />

java.sql.Statementに関する設定を行う

フェッチサイズやクエリタイムアウトなど、 java.sql.Statement に関する項目をプロジェクト全体に設定したい場合がある。

その場合はコンポーネント設定ファイルに DomaStatementProperties を設定する。

設定できる項目には下記のものがある。

  • 最大行数の制限値
  • フェッチサイズ
  • クエリタイムアウト(秒)
  • バッチサイズ

設定例を以下に示す。

ポイント
  • コンポーネント名は domaStatementProperties とすること
<component class="nablarch.integration.doma.DomaStatementProperties" name="domaStatementProperties">
  <!-- 最大行数の制限値を1000行に設定する -->
  <property name="maxRows" value="1000" />
  <!-- フェッチサイズを200行に設定する -->
  <property name="fetchSize" value="200" />
  <!-- クエリタイムアウトを30秒に設定する -->
  <property name="queryTimeout" value="30" />
  <!-- バッチサイズを400に設定する -->
  <property name="batchSize" value="400" />
</component>