JOOQ - DmitryGontarenko/usefultricks GitHub Wiki
JOOQ - библиотека, предназначенная для решения задач объектно-реляционного отображения (ORM).
Рассмотрим подключение и работу 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 законечно, можно приступать к неспосредственной работе с БД.
Для работы с БД в 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();
}
Baeldung. Spring Boot Support for jOOQ
Baeldung. Introduction to Jooq with Spring