«1. Введение

В этом руководстве мы кратко рассмотрим запуск приложения с помощью jOOQ (объектно-ориентированный запрос Java). Эта библиотека генерирует классы Java на основе таблиц базы данных и позволяет нам создавать безопасные для типов SQL-запросы через свободный API.

Мы рассмотрим всю настройку, подключение к базе данных PostgreSQL и несколько примеров операций CRUD.

2. Зависимости Maven

Для библиотеки jOOQ нам понадобятся следующие три зависимости jOOQ:

<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>jooq</artifactId>
    <version>3.13.4</version>
</dependency>
<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>jooq-meta</artifactId>
    <version>3.13.4</version>
</dependency>
<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>jooq-codegen</artifactId>
    <version>3.13.4</version>
</dependency>

Нам также понадобится одна зависимость для драйвера PostgreSQL:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.16</version>
</dependency>

3 .Структура базы данных

Прежде чем мы начнем, давайте создадим простую схему БД для наших примеров. Мы будем использовать простые отношения Author и Article:

create table AUTHOR
(
    ID         integer PRIMARY KEY,
    FIRST_NAME varchar(255),
    LAST_NAME  varchar(255),
    AGE        integer
);

create table ARTICLE
(
    ID          integer PRIMARY KEY,
    TITLE       varchar(255) not null,
    DESCRIPTION varchar(255),
    AUTHOR_ID   integer
        CONSTRAINT fk_author_id REFERENCES AUTHOR
);

4. Подключение к базе данных

Теперь давайте посмотрим, как мы будем подключаться к нашей базе данных.

Во-первых, нам нужно указать пользователя, пароль и полный URL-адрес базы данных. Мы будем использовать эти свойства для создания объекта Connection с помощью DriverManager и его метода getConnection:

String userName = "user";
String password = "pass";
String url = "jdbc:postgresql://db_host:5432/baeldung";
Connection conn = DriverManager.getConnection(url, userName, password);

Далее нам нужно создать экземпляр DSLContext. Этот объект будет нашей точкой входа для интерфейсов jOOQ:

DSLContext context = DSL.using(conn, SQLDialect.POSTGRES);

В нашем случае мы передаем диалект POSTGRES, но доступно несколько других, таких как H2, MySQL, SQLite и другие.

5. Генерация кода

Чтобы сгенерировать классы Java для наших таблиц базы данных, нам понадобится следующий файл jooq-config.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.13.0.xsd">
    
    <jdbc>
        <driver>org.postgresql.Driver</driver>
        <url>jdbc:postgresql://db_url:5432/baeldung_database</url>
        <user>username</user>
        <password>password</password>
    </jdbc>

    <generator>
        <name>org.jooq.codegen.JavaGenerator</name>

        <database>
            <name>org.jooq.meta.postgres.PostgresDatabase</name>
            <inputSchema>public</inputSchema>
            <includes>.*</includes>
            <excludes></excludes>
        </database>

        <target>
            <packageName>com.baeldung.jooq.model</packageName>
            <directory>C:/projects/baeldung/tutorials/jooq-examples/src/main/java</directory>
        </target>
    </generator>
</configuration>

Пользовательская конфигурация требует изменений в разделе \u003cjdbc\u003e, где мы помещаем учетные данные базы данных и в раздел \u003ctarget\u003e, в котором мы настраиваем имя пакета и каталог местоположения для классов, которые мы будем генерировать.

Чтобы запустить инструмент генерации кода jOOQ, нам нужно запустить следующий код:

GenerationTool.generate(
  Files.readString(
    Path.of("jooq-config.xml")
  )    
);

После завершения генерации мы получим два следующих класса, каждый из которых соответствует своей таблице базы данных:

com.baeldung.model.generated.tables.Article;
com.baeldung.model.generated.tables.Author;

6. Операции CRUD

Теперь давайте рассмотрим несколько основных операций CRUD, которые мы можем выполнять с библиотекой jOOQ.

6.1. Создание

Во-первых, давайте создадим новую запись статьи. Для этого нам нужно вызвать метод newRecord с соответствующей ссылкой на таблицу в качестве параметра:

ArticleRecord article = context.newRecord(Article.ARTICLE);

Переменная Article.ARTICLE является экземпляром ссылки на таблицу базы данных ARTICLE. Он был автоматически создан jOOQ во время генерации кода.

Далее мы можем установить значения для всех необходимых свойств:

article.setId(2);
article.setTitle("jOOQ examples");
article.setDescription("A few examples of jOOQ CRUD operations");
article.setAuthorId(1);

Наконец, нам нужно вызвать метод store для записи, чтобы сохранить ее в базе данных:

article.store();

6.2. Чтение

Теперь давайте посмотрим, как мы можем читать значения из базы данных. В качестве примера, давайте выберем всех авторов:

Result<Record> authors = context.select()
  .from(Author.AUTHOR)
  .fetch();

Здесь мы используем метод select в сочетании с предложением from, чтобы указать, из какой таблицы мы хотим читать. Вызов метода выборки выполняет запрос SQL и возвращает сгенерированный результат.

Объект Result реализует интерфейс Iterable, поэтому можно легко перебирать каждый элемент. Имея доступ к одной записи, мы можем получить ее параметры с помощью метода getValue с соответствующей ссылкой на поле:

authors.forEach(author -> {
    Integer id = author.getValue(Author.AUTHOR.ID);
    String firstName = author.getValue(Author.AUTHOR.FIRST_NAME);
    String lastName = author.getValue(Author.AUTHOR.LAST_NAME);
    Integer age = author.getValue(Author.AUTHOR.AGE);

    System.out.printf("Author %s %s has id: %d and age: %d%n", firstName, lastName, id, age);
});

Мы можем ограничить запрос на выборку набором определенных полей. Давайте получим только идентификаторы и заголовки статей:

Result<Record2<Integer, String>> articles = context.select(Article.ARTICLE.ID, Article.ARTICLE.TITLE)
  .from(Author.AUTHOR)
  .fetch();

Мы также можем выбрать один объект с помощью метода fetchOne. Параметрами для этого являются ссылка на таблицу и условие соответствия соответствующей записи.

В нашем случае давайте просто выберем автора с идентификатором, равным 1:

AuthorRecord author = context.fetchOne(Author.AUTHOR, Author.AUTHOR.ID.eq(1))

Если ни одна запись не соответствует условию, метод fetchOne вернет null.

6.3. Обновление

Чтобы обновить данную запись, мы можем использовать метод обновления из объекта DSLContext в сочетании с вызовами метода set для каждого поля, которое нам нужно изменить. За этими операторами должно следовать предложение where с надлежащим условием соответствия:

context.update(Author.AUTHOR)
  .set(Author.AUTHOR.FIRST_NAME, "David")
  .set(Author.AUTHOR.LAST_NAME, "Brown")
  .where(Author.AUTHOR.ID.eq(1))
  .execute();

Запрос на обновление будет выполняться только после того, как мы вызовем метод execute. В качестве возвращаемого значения мы получим целое число, равное количеству обновленных записей.

Также можно обновить уже полученную запись, выполнив ее метод store:

ArticleRecord article = context.fetchOne(Article.ARTICLE, Article.ARTICLE.ID.eq(1));
article.setTitle("A New Article Title");
article.store();

«

«Метод store вернет 1, если операция прошла успешно, или 0, если обновление не требуется. Например, ничего не соответствовало условию.

6.4. Удаление

context.delete(Article.ARTICLE)
  .where(Article.ARTICLE.ID.eq(1))
  .execute();

Чтобы удалить данную запись, мы можем использовать метод удаления из объекта DSLContext. Условие удаления должно быть передано в качестве параметра в следующем предложении where:

Запрос на удаление будет запущен только после того, как мы вызовем метод execute. В качестве возвращаемого значения мы получим целое число, равное количеству удаленных записей.

ArticleRecord articleRecord = context.fetchOne(Article.ARTICLE, Article.ARTICLE.ID.eq(1));
articleRecord.delete();

Также можно удалить уже извлеченную запись, выполнив ее метод удаления:

Метод удаления вернет 1, если операция прошла успешно, или 0, если удаление не требуется. Например, когда ничего не соответствовало условию.

7. Заключение