とりあえずメモ的に。
R2DBCとmicronaut+reactive-pg-clientの2通りでnative-imageを試したけど、nettyまわりでだめだったという話
R2DBC
リアクティブデータアクセスとしては本命?
R2DBC
R2DBCはセントラルリポジトリにないので、springのリポジトリを使います。
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
R2DBCのライブラリはこれ。
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>1.0.0.M6</version>
</dependency>
コードはこんな感じで。
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
public class Main {
public static void main(String[] args) throws InterruptedException {
PostgresqlConnectionConfiguration conf =
PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.database("mystat")
.username("mystat")
.password("pass").build();
PostgresqlConnectionFactory connFact =
new PostgresqlConnectionFactory(conf);
ExecutorService es = Executors.newSingleThreadExecutor();
Scheduler sc = Schedulers.fromExecutorService(es);
connFact.create()
.map(conn -> conn.createStatement("select * from USERS"))
.flatMapMany(stmt -> stmt.execute())
.flatMap(result -> result.map((row, meta) ->
String.format("handle:%s name:%s",
row.get("user_handle"), row.get("user_name"))))
.subscribeOn(sc)
.doOnTerminate(() -> es.shutdown())
.subscribe(System.out::println);
}
}
Schedulerにはelastic()
が楽なんだろうけど、コマンドラインだと処理が終わる前にコマンドが終了して何も出力されないので、ExecutorServiceを用意してfromExecutorService(es)
を使います。で、doOnTerminate
でshutdown()
。
もっといい方法があったら教えてください。
native-imageするのでJava8文法しか使えないからvar
はナシ。newとかbuilderとかはvar
で受けたいけど。
そしてnetty関係でダメでした。
R2DBC-client
R2DBCはちょっとラップして便利にしてくれるclientがあるので使ってみる。
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-client</artifactId>
<version>1.0.0.M6</version>
</dependency>
こんな感じ。ちょっと短くなった。
import io.r2dbc.client.R2dbc;
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
public class Main {
public static void main(String[] args) throws InterruptedException {
PostgresqlConnectionConfiguration conf =
PostgresqlConnectionConfiguration.builder()
.host("localhost")
.port(5432)
.database("mystat")
.username("mystat")
.password("pass").build();
PostgresqlConnectionFactory connFact =
new PostgresqlConnectionFactory(conf);
ExecutorService es = Executors.newSingleThreadExecutor();
Scheduler sc = Schedulers.fromExecutorService(es);
R2dbc r2dbc = new R2dbc(connFact);
r2dbc.inTransaction(h ->
h.select("select * from USERS")
.mapRow(row -> row.get("user_name")))
.subscribeOn(sc)
.doOnTerminate(() -> es.shutdown())
.subscribe(o -> System.out.println(o));
}
}
まあ、もとのR2DBCでこけてるので、もちろんnative-imageはnettyまわりでこける。
Reactive Postgres Client
もうひとつ、PostgreSQL用にはReactive Postgres Clientというのがある。
Reactive Postgres Client | reactive-pg-client
Micronaut経由で使ってみたらnative-image対応してくれてるんでは、と試してみる。
とはいえ、Webでやるのは面倒なので、CLIとして使ってみます。
Micronaut: 11. Standalone Command Line Applications
Micoronautのプロジェクト作成コマンドmnで大枠を作ります。
$ mn create-cli-app my-cli --features postgres-reactive
build.gradleにソースレベルなどの設定を追加
sourceCompatibility="1.8"
targetCompatibility="1.8"
application.ymlにPostgreSQL関係の設定がすでに書いてあるので、適切に変更。ここで、clientの下にあるべきものたちのインデントがちゃんとしてない気がするので修正してます。
postgres:
reactive:
client:
port: 5432
host: localhost
database: mystat
user: mystat
password: pass
修正のPRでも送るか~と思ったけど、ぺろぺろっとできる修正じゃなさそうなので保留
あとは、MyCliCommand.javaができてるはずなので、処理を書きます。
public void run() {
if (verbose) {
System.out.println("Hi!");
}
data();
}
@Inject
PgPool client;
public void data() {
client.rxQuery("select * from users")
.map(rowSet -> {
List<String> result = new ArrayList<>();
PgIterator ite = rowSet.iterator();
while(ite.hasNext()) {
Row row = ite.next();
result.add(row.getString("user_name"));
}
return result;
})
.blockingGet()
.stream()
.forEach(System.out::println);
}
println("Hi!")
は元からある処理。
PgPoolをInjectするだけで使えます。io.reactiverse.pgclient.PgPool
ではなくio.reactiverse.reactivex.pgclient.PgPool
なので注意。
しかし、R2DBCはReactorだけどReactive-pg-clientはRxJavaなので戸惑う。
そしてnative-imageしてみるけど、やはりnetty関係でだめだった。
そのうちリベンジする。