Doma2でデータベースから読み込み・書き出しする

Spring Batchで Doma2 を利用してデータを読み込み/書き出しする方法について説明します。

データベースアクセスにDoma2を利用するための基本的な設定については、 データベースアクセスにDoma2を使用する を参照してください。

以下のサンプルコードの動作確認環境については、 動作確認環境と依存ライブラリについて を参照してください。

データベースからの読み込み

バッチ処理では一般的に、処理対象データをすべて読み込んでヒープ上に展開することは推奨されません。そのため、少量ずつ読み込みながら順次ヒープ上に展開するための実装が必要になります。

データ読み込みに利用するDaoのメソッドは、Streamを返すように実装します。詳細については、以下の公式ドキュメントを参照してください。

ItemReaderではopenでDaoからStreamを取得し、Stream自身とそのStreamから取得したIteratorをインスタンス変数として保持しておきます。 readでは保持しているIteratorの次の要素を返すようにし、 closeでは保持しているStreamを閉じるようにします。

実装例

Dao (Reader)
@Dao
@ConfigAutowireable
public interface EmployeeBonusDao {
    @Select
    @Suppress(messages = Message.DOMA4274)
    Stream<EmployeeBonus> selectEmployeeBonus();
}
ItemStreamReader
@Component
public class EmployeeBonusReader extends AbstractItemCountingItemStreamItemReader<EmployeeBonus> {

    private final EmployeeBonusDao dao;
    private Stream<EmployeeBonus> stream;
    private Iterator<EmployeeBonus> iterator;

    public EmployeeBonusReader(EmployeeBonusDao dao) {
        super.setName(this.getClass().getSimpleName());
        this.dao = dao;
    }

    @Override
    protected EmployeeBonus doRead() {
        return iterator.hasNext() ? iterator.next() : null;
    }

    @Override
    protected void doOpen() {
        stream = dao.selectEmployeeBonus();
        iterator = stream.iterator();

    }

    @Override
    protected void doClose() {
        stream.close();
    }
}

サンプル全体は doma2-spring-batch-sample を参照してください。

警告

PostgreSQLでは、トランザクションが終了すると自動的にカーソルが閉じられます [1] 。そのため、Spring BatchのChunkのように一定間隔ごとにcommitされるような場合には、1つめのチャンクは正常に完了しますが、以降のチャンクは処理できなくなってしまいます。

このような事象を回避するために、データベースにアクセスするItemReaderの実装クラスは、 ItemStreamReader を実装したクラスにしてください。サンプルでは、ItemStreamReaderを実装した AbstractItemCountingItemStreamItemReader を継承しています。

ItemStreamReaderでは、openメソッドで利用されるコネクションとwriterで利用されるコネクションとは別のトランザクションに属するようになるため、上記の問題を踏まないようになっています。

データベースへの書き出し

ItemWriterではDoma2の@BatchInsertを利用して、一括でインサートします。詳細については、以下の公式ドキュメントを参照してください。

実装例

Dao (Writer)
@Dao
@ConfigAutowireable
public interface BonusDao {
    @BatchInsert
    BatchResult<Bonus> insert(List<Bonus> bonuses);
}
ItemWriter
@Component
public class BonusWriter implements ItemWriter<Bonus> {

    private final BonusDao dao;

    public BonusWriter(BonusDao dao) {
        this.dao = dao;
    }

    @Override
    public void write(Chunk<? extends Bonus> items) {
        dao.insert((List<Bonus>) items.getItems());
    }
}

サンプル全体は doma2-spring-batch-sample を参照してください。