← Заметки

Spring Boot 3 и Querydsl: настройка Jakarta и генерация Q-типов

На этой странице

Для генерации Q-типов Querydsl JPA в Spring Boot 3 согласуйте версии и классификаторы jakarta у querydsl-jpa и querydsl-apt, настройте обработку аннотаций, затем проверьте чистую сборку и тест запроса к БД. Примечания к выпуску 5.0 Querydsl описывают поддержку классификатора Jakarta. Стандартные пути Gradle для сгенерированного кода позволяют избежать записи основной и тестовой компиляции в один каталог.

Руководство развивает исходный корейский пример 2023 года до исполняемой конфигурации. Версии ниже закрепляют среду воспроизведения; это не рекомендации актуальных производственных зависимостей.

Порядок обработки

Генерация Q-типов и выполнение запроса

Процессор создаёт QEmployee при основной компиляции. Затем тест H2 выполняет запрос с этими сгенерированными типами.

Среда воспроизведения и полная настройка Gradle

Компонент Версия воспроизведения
JDK / Gradle 17.0.8.1 / 8.4
Spring Boot BOM 3.5.7
Querydsl JPA / APT 5.1.0, оба с классификатором jakarta
Hibernate / H2 6.6.33.Final / 2.3.232, управляются BOM

Создайте эти пять файлов в новом каталоге. Тестовый проект использует Java plugin и Boot BOM; Boot Gradle plugin ему не нужен. В существующее приложение Boot добавляйте только необходимые зависимости рядом с выбранным управлением зависимостями.

settings.gradle

rootProject.name = 'querydsl-jakarta-example'

build.gradle

plugins { id 'java' }
repositories { mavenCentral() }
dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:3.5.7')
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'

    annotationProcessor platform('org.springframework.boot:spring-boot-dependencies:3.5.7')
    annotationProcessor 'com.querydsl:querydsl-apt:5.1.0:jakarta'
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
    annotationProcessor 'jakarta.annotation:jakarta.annotation-api'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'com.h2database:h2'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test { useJUnitPlatform() }

Gradle разрешает annotationProcessor отдельно от обычных classpath компиляции и выполнения. Поэтому пример также применяет BOM к конфигурации процессора и явно предоставляет Jakarta Persistence и Annotation API. Зависимость implementation не становится доступной процессору автоматически. См. документацию Java plugin Gradle.

Создайте приложение и сущность

Класс приложения позволяет JPA-тесту обнаружить конфигурацию Boot. Для теста не требуется отдельный серверный процесс.

src/main/java/example/Application.java

package example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

src/main/java/example/Employee.java

package example;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    public String getName() { return name; }

    protected Employee() {}

    public Employee(String name) {
        this.name = name;
    }
}

Employee использует jakarta.persistence. Не пишите QEmployee вручную: процессор аннотаций создаёт тип, на который ссылается тест ниже.

Запрос через сгенерированный Q-тип

src/test/java/example/EmployeeQueryTest.java

package example;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
class EmployeeQueryTest {
    @Autowired
    private TestEntityManager entityManager;

    @Test
    void findsEmployeeByName() {
        entityManager.persistAndFlush(new Employee("Alice"));
        entityManager.persistAndFlush(new Employee("Bob"));
        QEmployee employee = QEmployee.employee;
        var names = new JPAQueryFactory(entityManager.getEntityManager())
            .select(employee.name).from(employee)
            .where(employee.name.eq("Alice")).fetch();
        assertThat(names).containsExactly("Alice");
    }

    @Test
    void returnsNoRowsForMissingName() {
        entityManager.persistAndFlush(new Employee("Alice"));
        QEmployee employee = QEmployee.employee;
        var names = new JPAQueryFactory(entityManager.getEntityManager())
            .select(employee.name).from(employee)
            .where(employee.name.eq("Missing")).fetch();
        assertThat(names).isEmpty();
    }
}

@DataJpaTest использует H2 из classpath примера. Каждый тест выполняется в транзакции с последующим откатом. После вставки через persistAndFlush сгенерированный QEmployee обеспечивает две проверки: точное совпадение имени и запрос без строк. См. руководство по JPA-тестированию Spring Boot.

Эти тесты проверяют эту сущность и запрос в H2. Они не проверяют SQL-диалект, правила сравнения строк, блокировки или миграции вашей производственной БД.

Запуск и проверка сгенерированных файлов

Выберите JDK 17 и установленный Gradle 8.4. Выполните из корня проекта:

java -version
gradle --version
gradle --no-daemon clean test

Проверьте эти сгенерированные артефакты и отчёт тестов:

build/generated/sources/annotationProcessor/java/main/example/QEmployee.java
build/classes/java/main/example/QEmployee.class
build/reports/tests/test/index.html

Оба теста прошли с этой конфигурацией. Повторный clean test заново создал Q-тип и снова прошёл оба теста. В существующем проекте с Gradle wrapper используйте выбранную им версию через ./gradlew clean test.

Q-тип в этом примере импортирует javax.annotation.processing.Generated — API Java SE из java.compiler. Этот импорт не требует замены на Jakarta. API Generated JDK 17.

Почему Q-типы отсутствуют или генерируются дважды?

Симптом Что проверить сначала
Не удаётся найти QEmployee Проверьте результат CLI clean compileJava, зависимости процессора и импорты jakarta.persistence сущности.
Процессор не находит Persistence API Проверьте необходимые Jakarta API в пути процессора, отдельно от обычного classpath.
Появляется несовместимый тип javax.persistence.EntityManager Проверьте совпадение версий и классификаторы Jakarta обоих артефактов Querydsl.
Дубликаты Q-типов или сборка работает только после clean Удалите написанные вручную Q-файлы и общие пути генерации; проверьте настройки вывода main/test.
CLI проходит, но IDE не видит Q-типы Проверьте синхронизацию Gradle, делегирование сборки и обработку аннотаций в IDE.

Пример не добавляет специальные настройки sourceSets, JavaCompile и clean для сгенерированных файлов. Если нужен собственный путь, разделяйте вывод main и test. configureEach управляет отложенной настройкой задач; он не исправляет совместимость зависимостей Jakarta. Избежание лишней конфигурации Gradle.

Общую последовательность обновления см. в руководстве по миграции Spring Boot 3. Для границ исходящего HTTP используйте тесты WebClient и MockWebServer.

Сравните инструменты до замены

Полный класс Employee включает getter для сравнения результатов репозитория по именам. Добавьте три файла из сравнения Querydsl и Specification, чтобы выполнить восемь дополнительных проверок той же сущности. Два исходных теста выше остаются частью проекта с десятью тестами. Это сравнение выбранных предикатов H2, а не всего поведения БД. Руководство Spring Boot связывает остальные шаги настройки и тестирования.

Обсуждение

Посмотреть на GitHub ↗