Как стать автором
Обновить

Реактивный SQL с jOOQ 3.15 и R2DBC

Java *
Перевод
Tutorial
Автор оригинала: lukaseder

Одна из самых больших новых функций недавно выпущенного 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

Теги:
Хабы:
Всего голосов 1: ↑1 и ↓0 +1
Просмотры 2.7K
Комментарии Комментировать