← 開発ノート

Spring Boot 3 と Querydsl:Jakarta 設定と Q 型生成

この記事の目次

Spring Boot 3 で Querydsl JPA の Q 型を生成するには、querydsl-jpa と querydsl-apt の版と jakarta 分類子をそろえ、注釈処理を設定してクリーンビルドと DB クエリテストを確認します。Querydsl の 5.0 リリースノートに Jakarta 分類子対応が記載されています。Gradle 既定の生成パスを使えば、main と test のコンパイルが同じディレクトリへ書く設定を避けられます。

このガイドは 2023 年の韓国語の例を実行可能な設定に発展させたものです。以下の版は再現環境を固定するもので、現在の本番依存関係の推奨ではありません。

処理の流れ

Q 型を生成してクエリを実行する流れ

main のコンパイル時にプロセッサーが 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 プラグインと Boot BOM を使い、Boot Gradle プラグインは不要です。既存の 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 を通常のコンパイル、実行時クラスパスとは別に解決します。そのため例ではプロセッサー設定にも BOM を適用し、Jakarta Persistence と Annotation API を明示的に与えます。implementation の依存は自動ではプロセッサーに渡りません。Gradle Java プラグインの文書を参照してください。

アプリとエンティティを作る

アプリのクラスにより 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 を使います。各テストはトランザクション内で実行し、終了後にロールバックします。persistAndFlush で挿入した後、生成された QEmployee で名前の完全一致と結果が空のクエリを確認します。Spring Boot の JPA テストガイドを参照してください。

このテストは H2 上のこのエンティティとクエリを確認します。本番 DB の 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 型は Java SE の java.compiler API である javax.annotation.processing.Generated を import します。Jakarta への置き換えは不要です。JDK 17 Generated API。

Q 型が見つからない、または重複生成される理由は?

症状 最初の確認
QEmployee がない CLI の clean compileJava 結果、プロセッサー依存、エンティティの jakarta.persistence import。
Persistence API が見つからない 通常のクラスパスと別のプロセッサーパスに必要な Jakarta API があるか。
非互換な javax.persistence.EntityManager 型が出る 両 Querydsl アーティファクトの版と Jakarta 分類子の一致。
Q 型が重複、または clean 後しか通らない 手書き Q ソースと共有生成パスを除去し、main/test 出力設定を調べる。
CLI は通るが IDE で見えない Gradle 同期、委任ビルド設定、IDE の注釈処理。

この例は生成ファイル用に sourceSets、JavaCompile、clean をカスタマイズしません。独自パスが必要なら main と test を分離します。configureEach は遅延タスク設定の制御であり、Jakarta 依存の互換性は直しません。Gradle の設定回避。

更新全体の順序は Spring Boot 3 移行ガイド、外向き HTTP の境界は WebClient と MockWebServer のテストを参照してください。

クエリツールの変更前に比較する

完全な Employee クラスには getter があり、リポジトリ結果を名前で比較できます。Querydsl と Specification の比較の三ファイルを加えると、同じエンティティで追加八件を実行できます。元の二件も計十件のプロジェクトに残ります。比較対象は選んだ H2 述語であり、全 DB 動作ではありません。Spring Boot ガイドが前後の設定とテストをつなぎます。

コメント

GitHubで見る ↗