最近发布的jOOQ 3.15最大的新功能之一是它对通过R2DBC进行反应式查询的新支持。这一直是一个非常受欢迎的功能请求,我们最终实现了这一点。
你可以继续以你习惯的方式使用jOOQ,为你提供类型安全的、嵌入在Java、kotlin或scala中的SQL,但你的查询执行不再是阻塞的。相反,你的jOOQ [ResultQuery](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html)
或 [Query](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/Query.html)
可以在你选择的reactive-streams实现中作为一个Publisher<R>
或Publisher<Integer>
。
除了配置你的jOOQ之外,还可以用JDBC [DSLContext](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/DSLContext.html)
配置一个JDBC [java.sql.Connection](https://docs.oracle.com/en/java/javase/16/docs/api/java.sql/java/sql/Connection.html)
或 [javax.sql.DataSource](https://docs.oracle.com/en/java/javase/16/docs/api/java.sql/javax/sql/DataSource.html)
,只需将其配置为R2DBC [io.r2dbc.spi.Connection](https://javadoc.io/static/io.r2dbc/r2dbc-spi/0.9.0.M2/io/r2dbc/spi/Connection.html)
或io.r2dbc.spi.ConnectionFactory
。
ConnectionFactory connectionFactory = ConnectionFactories.get(
ConnectionFactoryOptions
.parse("r2dbc:h2:file://localhost/~/r2dbc-test")
.mutate()
.option(ConnectionFactoryOptions.USER, "sa")
.option(ConnectionFactoryOptions.PASSWORD, "")
.build()
);
DSLContext ctx = DSL.using(connectionFactory);
另外,可以使用Spring Boot来自动配置jOOQ,就像这样:
当然,说得好:pic.twitter.com/tUgNkwzCK4
- Anghel Leonard (@anghelleonard)2021年7月15日
从这个DSLContext
,你可以像往常一样建立你的查询,但不是调用通常的阻塞式execute()
或fetch()
方法,而是直接将查询包裹在一个Flux
,例如。假设你在你的H2上运行了jOOQ代码生成器INFORMATION_SCHEMA
,你现在可以写:
record Table(String schema, String table) {}
Flux.from(ctx
.select(
INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA,
INFORMATION_SCHEMA.TABLES.TABLE_NAME)
.from(INFORMATION_SCHEMA.TABLES))
// Type safe mapping from Record2<String, String> to Table::new
.map(Records.mapping(Table::new))
.doOnNext(System.out::println)
.subscribe();
jOOQ将从你的ConnectionFactory
,获取一个R2DBCConnection
,并在查询执行完毕后再次释放它,从而实现优化的资源管理,这在R2DBC和reactor中是有点棘手的。换句话说,上面的执行与这个手动编写的查询是对应的:
Flux.usingWhen(
connectionFactory.create(),
c -> c.createStatement(
"""
SELECT table_schema, table_name
FROM information_schema.tables
"""
).execute(),
c -> c.close()
)
.flatMap(it -> it.map((r, m) ->
new Table(r.get(0, String.class), r.get(1, String.class))
))
.doOnNext(System.out::println)
.subscribe();
两者都会打印出类似下面的内容:
Table[schema=INFORMATION_SCHEMA, table=TABLE_PRIVILEGES]
Table[schema=INFORMATION_SCHEMA, table=REFERENTIAL_CONSTRAINTS]
Table[schema=INFORMATION_SCHEMA, table=TABLE_TYPES]
Table[schema=INFORMATION_SCHEMA, table=QUERY_STATISTICS]
Table[schema=INFORMATION_SCHEMA, table=TABLES]
Table[schema=INFORMATION_SCHEMA, table=SESSION_STATE]
Table[schema=INFORMATION_SCHEMA, table=HELP]
Table[schema=INFORMATION_SCHEMA, table=COLUMN_PRIVILEGES]
Table[schema=INFORMATION_SCHEMA, table=SYNONYMS]
Table[schema=INFORMATION_SCHEMA, table=SESSIONS]
Table[schema=INFORMATION_SCHEMA, table=IN_DOUBT]
Table[schema=INFORMATION_SCHEMA, table=USERS]
Table[schema=INFORMATION_SCHEMA, table=COLLATIONS]
Table[schema=INFORMATION_SCHEMA, table=SCHEMATA]
Table[schema=INFORMATION_SCHEMA, table=TABLE_CONSTRAINTS]
Table[schema=INFORMATION_SCHEMA, table=INDEXES]
Table[schema=INFORMATION_SCHEMA, table=ROLES]
Table[schema=INFORMATION_SCHEMA, table=FUNCTION_COLUMNS]
Table[schema=INFORMATION_SCHEMA, table=CONSTANTS]
Table[schema=INFORMATION_SCHEMA, table=SEQUENCES]
Table[schema=INFORMATION_SCHEMA, table=RIGHTS]
Table[schema=INFORMATION_SCHEMA, table=FUNCTION_ALIASES]
Table[schema=INFORMATION_SCHEMA, table=CATALOGS]
Table[schema=INFORMATION_SCHEMA, table=CROSS_REFERENCES]
Table[schema=INFORMATION_SCHEMA, table=SETTINGS]
Table[schema=INFORMATION_SCHEMA, table=DOMAINS]
Table[schema=INFORMATION_SCHEMA, table=KEY_COLUMN_USAGE]
Table[schema=INFORMATION_SCHEMA, table=LOCKS]
Table[schema=INFORMATION_SCHEMA, table=COLUMNS]
Table[schema=INFORMATION_SCHEMA, table=TRIGGERS]
Table[schema=INFORMATION_SCHEMA, table=VIEWS]
Table[schema=INFORMATION_SCHEMA, table=TYPE_INFO]
Table[schema=INFORMATION_SCHEMA, table=CONSTRAINTS]
注意,如果你使用的是JDBC而不是R2DBC,你可以继续以阻塞的方式使用jOOQ API与你的反应式流库,方法与上面完全一样,例如,如果你喜欢的RDBMS还不支持反应式R2DBC驱动。根据r2dbc.io,目前支持的驱动包括:
所有这些我们都用jOOQ 3.15+进行集成测试。

一个可运行的例子
去玩一下这里的例子:https://github.com/jOOQ/jOOQ/tree/main/jOOQ-examples/jOOQ-r2dbc-example
它使用下面的模式:
CREATE TABLE r2dbc_example.author (
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
CONSTRAINT pk_author PRIMARY KEY (id)
);
CREATE TABLE r2dbc_example.book (
id INT NOT NULL AUTO_INCREMENT,
author_id INT NOT NULL,
title VARCHAR(100) NOT NULL,
CONSTRAINT pk_book PRIMARY KEY (id),
CONSTRAINT fk_book_author FOREIGN KEY (id)
REFERENCES r2dbc_example.author
);
并运行以下代码:
Flux.from(ctx
.insertInto(AUTHOR)
.columns(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.values("John", "Doe")
.returningResult(AUTHOR.ID))
.flatMap(id -> ctx
.insertInto(BOOK)
.columns(BOOK.AUTHOR_ID, BOOK.TITLE)
.values(id.value1(), "Fancy Book"))
.thenMany(ctx
.select(
BOOK.author().FIRST_NAME,
BOOK.author().LAST_NAME,
BOOK.TITLE)
.from(BOOK))
.doOnNext(System.out::println)
.subscribe();
插入两条记录并获取连接的结果,如下所示:
+----------+---------+----------+
|FIRST_NAME|LAST_NAME|TITLE |
+----------+---------+----------+
|John |Doe |Fancy Book|
+----------+---------+----------+