Одна из самых больших новых функций недавно выпущенного jOOQ 3.15 - это поддержка реактивных запросов с помощью R2DBC. Это был очень популярный запрос функциональности, и мы наконец его выполнили.
Вы можете продолжать использовать jOOQ так, как вы привыкли, обеспечивая типобезопасный SQL, встроенный в Java, kotlin или scala, но выполнение ваших запросов больше не блокируется. Вместо этого ваш jOOQ ResultQuery
или Query
может использоваться как Publisher<R>
или Publisher<Integer>
в реализации reactive-streams (реактивных потоков) по вашему выбору.
Вместо (или в дополнение к) настройки вашего jOOQ DSLContext
с помощью JDBC java.sql.Connection
или javax.sql.DataSource
просто настройте его с помощью R2DBC io.r2dbc.spi.Connection
или 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
- Ангел Леонард (@anghelleonard) 15 июля 2021 г.
Используя этот DSLContext
, вы можете строить свои запросы как обычно, но вместо того, чтобы вызывать обычные блокирующие методы execute()
или fetch()
, вы просто оберните запрос в Flux
, например. Предположим, что вы запустили генератор кода jOOQ на своей H2 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 получит R2DBC Connection
у ConnectionFactory
и освободит ее после выполнения запроса, что позволит оптимизировать управление ресурсами, что в противном случае несколько сложно с 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 с библиотеками реактивных потоков в режиме блокировки точно так же, как указано выше, например, если ваша любимая СУБД еще не поддерживает реактивный драйвер 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 |