JOOQ - DmitryGontarenko/usefultricks GitHub Wiki

About

JOOQ - библиотека, предназначенная для решения задач объектно-реляционного отображения (ORM).

Configuration

Рассмотрим подключение и работу JOOQ вместе со Spring Boot.

Подключаем зависимость:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jooq</artifactId>
        </dependency>

Настраиваем плагин для генерации сущностей:

<build>
        <plugins>
            <!-- JOOQ Generator Plugin -->
            <plugin>
                <groupId>org.jooq</groupId>
                <artifactId>jooq-codegen-maven</artifactId>
                <version>3.13.2</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <jdbc>  <!-- конфигурация БД -->
                        <driver>org.postgresql.Driver</driver>
                        <url>jdbc:postgresql://localhost:5432/jooq</url>
                        <user>postgres</user>
                        <password>123</password>
                    </jdbc>
                    <generator>
                        <database>
                            <includes>.*</includes>  <!-- включаемые таблицы -->
                            <excludes>  <!-- исключаемые таблицы -->
                                flyway_schema_history
                            </excludes>
                            <inputSchema>public</inputSchema>  <!-- схема -->
                        </database>
                        <generate>
                            <records>true</records>
                        </generate>
                        <target>
                            <!-- структура пакетов в указанной ниже директории -->
                            <packageName>com.home.jooq.db.autocreated</packageName>
                            <!-- директория для хранения сформированных классов -->
                            <directory>src/main/java</directory>
                        </target>
                    </generator>
                </configuration>
            </plugin>
        </plugins>
    </build>

После настройки этого плагина, появляется возможность автоматической генерации сущностей из таблиц БД.
Для этого нужно в Intellij Idea открыть вкладку Maven - [project_name] - Plugins - jooq-codegen - jooq-codegen:generate. После этого, все сгенерированные классы будут помещены в указанную директорию (или в target, по умолчанию).

Настроиваем подключение к БД в application.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/jooq
    username: postgres
    password: 123

Создадим класс с конфигурацией:

@Configuration
@EnableTransactionManagement
public class JooqConfiguration {
    @Autowired
    private DataSource dataSource;

    @Bean
    public DataSourceConnectionProvider connectionProvider() {
        return new DataSourceConnectionProvider
                (new TransactionAwareDataSourceProxy(dataSource));
    }

    @Bean
    public DefaultDSLContext dsl() {
        return new DefaultDSLContext(configuration());
    }

    @Bean
    public DefaultConfiguration configuration() {
        DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
        jooqConfiguration.set(connectionProvider());
        jooqConfiguration.set(new DefaultExecuteListenerProvider(new ExceptionTranslator()));
        jooqConfiguration.set(SQLDialect.POSTGRES);

        return jooqConfiguration;
    }

}

С помощью аннотации @EnableTransactionManagement передаем управления транзакциями Spring'у.
Spring Boot автоматически настроил bean-компонент dataSource на основе свойств, установленных в файле application.yml, поэтому нет необходимости создавать его вручную. Нам остается только внедрить его с помощью аннотациии @Autowired.
В методе configuration() настраиваем конфигурацию - указываем провайдера БД, переводчик исключений (пример ниже), а также SQL-диалект.

Важно установить SQL-диалект для конкретной БД, иначе некоторые команды будут неправильно распознаны. Например, без указания диалекта и попытке вызывать определнную последовательность dslContext.nextval("language_seq") в PostgreSQL - JOOQ будет пытаться выполнить такой скрипт - select "public"."language_seq".nextval, что является невалидным для PostgreSQL и вызовет ошибку времени выполнения.

Определим реализацию интерфейса ExecuteListener, что бы Spring мог правильно обрабатывать исключения, возникающие при выполнении JOOQ-запросов:

public class ExceptionTranslator extends DefaultExecuteListener {
    public void exception(ExecuteContext context) {
        SQLDialect dialect = context.configuration().dialect();
        SQLExceptionTranslator translator = new SQLErrorCodeSQLExceptionTranslator(dialect.name());
        context.exception(translator.translate("Access database using jOOQ", context.sql(), context.sqlException()));
    }
}

Конфигурирование JOOQ законечно, можно приступать к неспосредственной работе с БД.

Queries

Для работы с БД в DAO-слое необоходимо вндерить зависимость:

    @Autowired
    private DSLContext dslContext;

Рассмотрим примеры запросов.
Создадим мапперы:

    private static final RecordMapper<Record, Book> BOOK_MAPPER =
            new RecordMapper<Record, Book>() {
                @Override
                public Book map(Record record) {
                    Book book = new Book();
                    book.setId(record.getValue(BOOK.ID));
                    book.setTitle(record.getValue(BOOK.TITLE));

                    return book;
                }
            };

    private static final RecordMapper<Record, Author> AUTHOR_MAPPER =
            record -> {
                Author author = new Author();
                author.setId(record.getValue(AUTHOR.ID));
                author.setFirstName(record.getValue(AUTHOR.FIRST_NAME));
                author.setLastName(record.getValue(AUTHOR.LAST_NAME));

                return author;
            };

RecordMapper является функциональным интерфейссом (т.е. содержит только один метод), поэтому мы можем воспользоваться лямбда-выражением и заменить запись на более короткую.

SELECT запросы:

    public List<Book> getBooks() {
        return dslContext.select(
                BOOK.ID,
                BOOK.TITLE)
                .from(BOOK)
                .fetch(BOOK_MAPPER);
    }

    private Book getBookRussianById(Long id) {
        return dslContext.select(
                BOOK.ID,
                BOOK.TITLE)
                .from(BOOK)
                .where(BOOK.ID.eq(id).and(BOOK.LANGUAGE.eq(Language.RUSSIAN.toString())))
                .fetchOne(BOOK_MAPPER);
    }

    public Author getAuthorByBook(String book) {
        return dslContext.select(
                AUTHOR.ID,
                AUTHOR.FIRST_NAME,
                AUTHOR.LAST_NAME)
                .from(AUTHOR)
                .join(AUTHOR_BOOK_CRS).on(AUTHOR_BOOK_CRS.AUTHOR_ID.eq(AUTHOR.ID))
                .join(BOOK).on(BOOK.ID.eq(AUTHOR_BOOK_CRS.BOOK_ID))
                .where(BOOK.TITLE.eq(book))
                .fetchOne(AUTHOR_MAPPER);
    }

INSERT запросы:

    public void saveBook(String book) {
        Long bookNextval = dslContext.nextval(BOOK_SEQ);

        dslContext.insertInto(BOOK)
                .set(BOOK.ID, bookNextval)
                .set(BOOK.TITLE, book)
                .execute();
    }

UPDATE запросы:

    public void updateBookById(Long id, String newBook) {
        dslContext.update(BOOK)
                .set(BOOK.TITLE, newBook)
                .where(BOOK.ID.eq(id))
                .execute();
    }

DELETE запросы:

    public void deleteBookById(Long id) {
        dslContext.delete(BOOK)
                .where(BOOK.ID.eq(id))
                .execute();
    }

Sources

Baeldung. Spring Boot Support for jOOQ
Baeldung. Introduction to Jooq with Spring

⚠️ **GitHub.com Fallback** ⚠️