怖くないR2DBC その2 r2dbc-client

これのつづき。

bufferings.hatenablog.com

## 1.0.0.M6

数日前に1.0.0.M6が出たので、この前はM5のソースを読んでたけど今日はM6のにする。

## r2dbc-clientを見てみる

https://github.com/r2dbc/r2dbc-client/tree/master/src/main/java/io/r2dbc/client

r2dbc-clientはr2dbc-spiを人が使いやすいようにしてくれるためのシンプルなライブラリー。とりあえずは、この2つのクラスを見ておけば良さそうかな?

  • Handle
  • R2dbc

## Handle

Connectionのラッパー。結果をPublisherじゃなくてMonoとかFluxで返してくれたり、ちょこちょこ便利にしてくれてるっぽい。

## R2dbc

このクラスがr2dbc-clientを使うときの中心になる。コネクションファクトリーを渡してインスタンスを生成。

R2dbc r2dbc = new R2dbc(new PostgresqlConnectionFactory(configuration));

使うかなーと思うのは次の4つ。

  • withHandle:ハンドルを使って処理をする。結果を返すやつ用。
  • useHandle:ハンドルを使って処理をする。結果を返さないやつ用。
  • inTransactionトランザクション内で処理をする。結果を返すやつ用。
  • useTransactionトランザクション内で処理をする。結果を返さないやつ用。

どれも、流れ的には

  1. コネクションをファクトリーから生成してハンドルを開く
  2. 処理をする
  3. ハンドルを閉じる

な感じ。エラーハンドリングもしてる。

### withHandle

というか、どれも最終的にはwithHandleを使ってて、そこに上記の処理が書いてある感じ。

    public <T> Flux<T> withHandle(Function<Handle, ? extends Publisher<? extends T>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return open()
            .flatMapMany(handle -> Flux.from(
                f.apply(handle))
                .concatWith(ReactiveUtils.typeSafe(handle::close))
                .onErrorResume(ReactiveUtils.appendError(handle::close)));
    }

へー。エラー処理はonErrorResumeってのを使うんだねー。

### useHandle

へー。then()Mono<Void>を返すようにするのかー。

    public Mono<Void> useHandle(Function<Handle, ? extends Publisher<?>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return withHandle(f)
            .then();
    }

### inTransaction

トランザクション内で処理をする」っていうハンドルのメソッドを呼び出してるだけ。

    public <T> Flux<T> inTransaction(Function<Handle, ? extends Publisher<? extends T>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return withHandle(handle -> handle.inTransaction(f));
    }

### useTransaction

それのuseHandle版。

    public Mono<Void> useTransaction(Function<Handle, ? extends Publisher<?>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return useHandle(handle -> handle.useTransaction(f));
    }

## ということで

Handleを引数にして処理をする関数を書けば良いみたいね。前回書いたこのコードは

  private Flux<String> sample() {
    var connectionFactory = getPostgresqlConnectionFactory();
    return connectionFactory.create()
        .flatMapMany(connection ->
            connection.createStatement("SELECT city FROM weather")
                .execute()
                .flatMap(result ->
                    result.map((row, metadata) ->
                        row.get("city", String.class)
                    )
                )
        );
  }

R2dbcクラスを使うと、こんな感じで書ける。この例だとあんまり変わらないけど、トランザクションとか使ってるともっと違うんかな。

  private Flux<String> sampleR2dbc() {
    var connectionFactory = getPostgresqlConnectionFactory();
    var r2dbc = new R2dbc(connectionFactory);
    return r2dbc.withHandle(h ->
        h.createQuery("SELECT city FROM weather").mapRow(
            (row, metadata) ->
                row.get("city", String.class)
        ));
  }

面白いなー。