QueryDSLとは?Spring Bootでの導入手順と5.1.0・OpenFeign版7.5の選び方
QueryDSLは、JPQLの文字列をJavaのメソッド呼び出しに置き換え、クエリの誤りをコンパイル時に検出するためのライブラリです。導入前に押さえておきたいのは、2026年7月現在このライブラリが二系統に分かれている点になります。
本家のcom.querydslはMaven Centralで2024年1月29日公開の5.1.0を最後にリリースが止まりました。継続的に更新されているのはOpenFeign配下のフォークio.github.openfeign.querydsl(7.5、2026年7月21日)です。それでもSpring Boot 4.1のBOMが管理するバージョンは5.1.0のまま。単純に新しいほうを選べばよいとは言えません。この記事では、どちらを選ぶかの判断基準と、Spring Boot 3/4での正しい依存関係の書き方、DTO射影・動的クエリ・JOINの実装までを、公式リポジトリとMaven Centralで確認できる事実に絞って整理します。
まとめ
- 本家
com.querydslのMaven Central最新版は5.1.0(2024年1月29日)。以降のリリースは無く、JDK 21対応の修正もマージ済みのまま未リリースです。 - 活発に更新されているのはOpenFeign版7.5(2026年7月21日)。Java 17以上・Jakarta EE専用ですが、Javaのパッケージ名は
com.querydsl.*のままなのでimport文の書き換えは不要です。 - Spring Boot 3.5・4.1のBOMが管理するのは5.1.0。Spring Bootの管理下で運用するなら本家5.1.0が既定で、フォークへ移るのは目的がある場合に限られます。
- Spring Boot 3以降では
jakarta分類子付きの成果物を指定します。付け忘れるとjavax.persistenceを参照する古いjarが入り、Qクラス生成やクエリ実行が失敗します。 - Mavenでは分類子を付けた依存がBOMの管理キーと一致しないため、
<version>の明記が必須です。 - Hibernate 6以降はfetch join時の重複排除が自動で行われます。
distinct()を残してもSQLにDISTINCTが出力されるだけです。 - 件数取得の
fetchCount()とfetchResults()は5.xで非推奨。件数用のクエリを別に組み立てます。
以下、仕組みの理解から順に、導入設定・実装パターン・落とし穴を見ていきます。
QueryDSLの仕組み:Qクラスによる型安全なクエリ構築
QueryDSLはエンティティクラスをアノテーションプロセッサで走査し、Bookに対してQBookのような「Qクラス」をビルド時に生成します。クエリはこのQクラスのフィールドを起点に組み立てるため、存在しないカラム名やありえない型比較はコンパイルエラーになります。JavaのQueryDSLはJPA専用ではありません。エンティティを介さずSQLを直接組み立てるquerydsl-sql、MongoDB向けのquerydsl-mongodbなど、バックエンドごとにモジュールが分かれており、いずれも同じ書き味のAPIを提供します。どのO/Rマッパーを土台に据えるかを含めて検討している段階なら、Java ORM(O/Rマッパー)の種類と選び方で候補を整理してから読み進めると判断が早くなります。
文字列JPQLとの違いはエラーを見つけるタイミング
JPQLを文字列で書いた場合、綴りの誤りやパラメータ名の不一致はアプリケーションが該当クエリを実行した瞬間まで表面化しません。QueryDSLではその大半がコンパイル時に落ちます。差が最も効くのはリファクタリング時です。エンティティのフィールド名をpublishedAtからpublishedOnへ変更すると、文字列JPQLは無傷のまま実行時例外になりますが、QueryDSLはQクラスの再生成後にビルドが失敗して変更漏れを教えてくれます。一方で、静的な検索条件しか無く条件分岐も発生しない画面では、Spring Data JPAのメソッド名クエリのほうが記述量は少なく済みます。QueryDSLを入れる価値が出るのは、条件が実行時に決まるクエリを抱えたときです。
Elasticsearchの「Query DSL」との同名異義
検索結果には、Elasticsearchで検索条件をJSONで記述する「Query DSL」の解説が混ざります。名前が似ているだけで無関係な別技術です。この記事が扱うのはJava向けのQueryDSLで、Elasticsearchの検索クエリを組み立てるものではありません。Kotlin向けのKomapperなど、ドキュメント内で「QueryDSL」という語を独自の意味で使っているライブラリもあります。参考記事を読むときはgroupIdがcom.querydsl系かどうかを確認すると、取り違えを避けられます。
本家5.1.0とOpenFeign版7.5の選択基準
ここが導入判断で最も情報の少ない部分です。日本語の解説記事の多くは5.0.0時代に書かれており、プロジェクトが分裂した事実に触れていません。まず双方の現状を数字で押さえます。
本家 com.querydsl の停滞状況
Maven Centralのcom.querydsl:querydsl-jpaおよびquerydsl-coreのメタデータでは、最新版・リリース版ともに5.1.0で、更新時刻は2024年1月29日です。GitHubのquerydsl/querydslリポジトリはアーカイブされていないものの、マージされた最後のプルリクエストは2025年2月1日でした。象徴的なのは、JDK 21での互換性を確保するプルリクエスト(#3705)が2024年5月11日にマージされているにもかかわらず、それを含むリリースが一度も出ていない点です。つまり5.1.0はこの修正より前のビルドになります。公式サイトのquerydsl.comもGitHub Pagesの証明書がドメインに対応しておらず、ブラウザで開くと証明書エラーの警告が出ます。参照するならGitHubリポジトリが確実です。
OpenFeign版で変わる点と据え置かれる点
| 項目 | 本家 5.1.0 | OpenFeign 7.5 |
|---|---|---|
| groupId | com.querydsl | io.github.openfeign.querydsl |
| Javaパッケージ名 | com.querydsl.* | com.querydsl.*(据え置き) |
| artifactId | querydsl-jpa ほか | 同一(据え置き) |
| 必要なJDK | Java 8以上 | Java 17以上 |
| 名前空間 | javax / jakarta を分類子で選択 | 6.0以降 jakarta のみ |
| 削除モジュール | なし | jdo / lucene3・4・5 / hibernate-search |
| 追加モジュール | なし | querydsl-r2dbc / querydsl-kotlin |
Javaのパッケージ名とartifactIdが据え置かれているため、乗り換えてもアプリケーション側のimport文とクエリのコードは変わりません。リリースの間隔には波があり、7.1(2025年10月21日)から7.2(2026年5月25日)まで7か月空いた後、7.3.0・7.4.0・7.5と2026年5月以降は月1本前後のペースに戻っています。
OpenFeign版へ乗り換えるときのビルド定義
// OpenFeign 版へ移す場合、ビルド定義で変わるのは groupId とバージョンだけ
implementation 'io.github.openfeign.querydsl:querydsl-jpa:7.5'
annotationProcessor 'io.github.openfeign.querydsl:querydsl-apt:7.5:jpa'
書き換えるのはgroupIdとバージョンだけです。ただしMavenを使っている場合は、移行ガイドがcom.mysema.maven:apt-maven-pluginを非推奨としており、後述のmaven-compiler-plugin方式へ合わせる必要があります。
Spring Boot前提での既定は本家5.1.0
判断の決め手はSpring BootのBOMです。spring-boot-dependenciesの3.5系でも最新の4.1.0でも、管理されているのはcom.querydsl:querydsl-bomの5.1.0です。バージョンを自分で決めずに済む側が本家である以上、Spring Bootに追随する運用なら既定は5.1.0になります。ただし過信は禁物で、querydsl-jpa 5.1.0自体はHibernate 5.4.8.Finalに対してビルドされた成果物です。Spring Boot 4.1ではHibernate 7.4.1.Final・Jakarta Persistence 3.2.0と同居することになるため、Hibernate固有のAPIを直接呼ぶ箇所があるなら自前の検証を挟んでください。
フォークへ移る理由があるのは、R2DBCによるノンブロッキングアクセスやKotlin向け拡張が必要な場合、あるいは自分でパッチを送って取り込んでもらいたい場合です。逆に、既存のJPAアプリケーションが5.1.0で動いていてSpring Bootのバージョンアップへの追随を優先するなら、移行するべきではありません。削除されたquerydsl-jdoやquerydsl-lucene5を使っているプロジェクトも同様です。フォークの作者自身がREADMEで「本家が保守を再開すればこのフォークはアーカイブする可能性が高い」と述べており、長期の保守先として確定しているわけではない点も織り込んでおく必要があります。Spring Boot側の期限と合わせた計画はSpring Bootバージョン一覧とサポート期限で確認できます。
Spring Boot 3/4への導入設定
以下は本家5.1.0を前提にした設定です。Spring Boot 4での変更点全般はSpring Boot 4とは?最新バージョン4.1の変更点にまとめています。
Gradleでの依存関係とjakarta分類子
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.16'
id 'io.spring.dependency-management' version '1.1.7'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// jakarta 分類子付きの成果物を指定する(Spring Boot 3 以降は必須)
implementation 'com.querydsl:querydsl-jpa:5.1.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.1.0:jakarta'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
com.querydsl:querydsl-jpa:5.1.0:jakartaの末尾がjakarta分類子です。Maven Centralの5.1.0にはquerydsl-jpa-5.1.0.jarとquerydsl-jpa-5.1.0-jakarta.jarの両方が置かれており、分類子を省くと前者、つまりjavax.persistenceを参照するjarが入ります。Spring Boot 3はJakarta EEへ移行済みなので、querydsl-aptにも同じ分類子が必要。ここを取り違えると、Qクラスが生成されないか、生成されても実行時にNoClassDefFoundErrorで落ちます。
Gradleで長く使われてきたcom.ewerk.gradle.plugins.querydslプラグインは不要です。提供元のewerk/gradle-pluginsリポジトリはアーカイブ済みで、最後の更新は2021年4月28日。READMEにも「Gradle 5以降とは互換性がなく、JDK 1.8より上ではテストしていない」と明記されています。上のようにannotationProcessor設定だけで完結します。
Mavenでversionの明記が必須になる理由
<!-- 前提: spring-boot-starter-parent 3.5.16 を親に持つ pom.xml -->
<properties>
<java.version>21</java.version>
<querydsl.version>5.1.0</querydsl.version>
</properties>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<classifier>jakarta</classifier>
<!-- classifier 付きは BOM の管理キーと一致しないため version が要る -->
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</path>
<path>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<!-- Boot 3.5 系は 3.1.0、Boot 4.1 系は 3.2.0 -->
<version>3.1.0</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<version>を省略できないのには理由があります。Mavenの依存関係管理はgroupId:artifactId:type:classifierをキーとして照合し(Dependency.getManagementKey()の実装)、一方でquerydsl-bom 5.1.0が管理しているエントリには分類子が一切付いていません。その結果、jakarta分類子を付けた依存はBOMの管理対象と別物として扱われ、バージョンを書かないと解決に失敗します。Spring Bootを使っていればバージョンは省略できるはず、と考えて省くとビルドが通らない。これがMaven側の典型的な詰まりどころです。
アノテーションプロセッサの指定にも注意点があります。maven-compiler-pluginの公式ドキュメントは、annotationProcessorPathsを指定した場合コンパイラはそこに挙げた要素からのみアノテーションプロセッサを検出する、と明記しています。Lombokを通常の依存関係として入れているプロジェクトでは、同じannotationProcessorPathsにLombokを並べない限り、QueryDSL導入を境にLombokが無効化されます。
Qクラスが生成されないときの確認箇所
- 分類子の付け忘れ:
querydsl-apt側にjakartaが付いているか。Spring Boot 3以降で最も多い原因です。 - エンティティのアノテーション:
jakarta.persistence.Entityを付けているか。javax.persistence.Entityのままだと走査対象になりません。 - 生成先がソースセットに入っていない:Gradleは
build/generated/sources/annotationProcessor配下に出力します。IDEが認識しない場合はプロジェクトの再インポートで解消することが多いです。 - 古い生成物の残存:エンティティ名を変えた後は
cleanを挟まないと、削除済みのQクラスが参照され続けます。
4つとも該当しない場合は、ビルドログにアノテーションプロセッサの実行行が出ているかを先に確認します。出ていなければ依存関係の指定、出ているのにQクラスが無ければ出力先の設定が原因です。
JPAQueryFactoryの登録とSpring Data JPAとの連携
QueryDSLでJPAのクエリを発行する入口はJPAQueryFactoryです。EntityManagerを渡して生成し、Beanとして登録しておくとリポジトリから注入できます。
JPAQueryFactoryのBean定義
@Configuration
public class QuerydslConfig {
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
}
}
メソッド引数で受け取るEntityManagerはプロキシで、トランザクションごとに実際のインスタンスへ委譲されます。そのためJPAQueryFactoryをシングルトンとして登録して問題ありません。DIやJPA関連のアノテーションの役割を確認したい場合はSpring Bootの主要なアノテーション一覧が参考になります。
フラグメント方式でのカスタムリポジトリ実装
// フラグメントインターフェース
public interface BookRepositoryCustom {
List<Book> search(String titleKeyword, Integer minPrice);
}
// 実装クラス名は「フラグメント名 + Impl」
@RequiredArgsConstructor
public class BookRepositoryCustomImpl implements BookRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public List<Book> search(String titleKeyword, Integer minPrice) {
QBook book = QBook.book;
return queryFactory
.selectFrom(book)
.where(
titleContains(book, titleKeyword),
priceGoe(book, minPrice)
)
.orderBy(book.publishedOn.desc())
.limit(50)
.fetch();
}
private BooleanExpression titleContains(QBook book, String keyword) {
return StringUtils.hasText(keyword) ? book.title.contains(keyword) : null;
}
private BooleanExpression priceGoe(QBook book, Integer minPrice) {
return minPrice != null ? book.price.goe(minPrice) : null;
}
}
// リポジトリ本体にフラグメントを継承させる
public interface BookRepository extends JpaRepository<Book, Long>, BookRepositoryCustom {
}
実装クラス名は、リポジトリ名ではなくフラグメントインターフェース名+Implにします。リポジトリ名から導出するBookRepositoryImplという命名も動きはしますが、Spring Dataのリファレンスはこの単一実装向けの命名を非推奨とし、フラグメント方式への移行を勧めています。
where()に複数の条件をカンマ区切りで渡すと、それぞれがANDで連結されます。ここで効いてくるのが、BooleanExpressionを返すメソッドがnullを返した条件はwhere()側で無視される仕様です。上のtitleContainsのように、値があるときだけ条件を足す処理をif文なしで書けます。条件メソッドを小さく切り出しておけば、別のクエリからも再利用できます。
QuerydslPredicateExecutorとの使い分け
Spring Data JPAにはQuerydslPredicateExecutorというインターフェースもあり、リポジトリに継承させるだけでfindAll(Predicate)などが使えます。実装クラスを書かずに済むのは利点ですが、返せるのはエンティティ全体だけです。次章で扱うDTO射影や集計、JOINの明示的な制御はできません。単純な絞り込み検索ならQuerydslPredicateExecutor、射影やfetch joinの制御が要るならJPAQueryFactoryを使ったカスタムリポジトリ、と分けるのが実務的です。両方を1つのリポジトリに継承させても競合しません。
BooleanBuilderによる動的クエリの組み立て
検索条件が実行時に決まる画面では、条件を順に積み上げられるBooleanBuilderが使えます。前掲のBooleanExpressionを返す方式と比べると、ループの中で条件を足していく手続き的な組み立てに向いています。
QBook book = QBook.book;
BooleanBuilder condition = new BooleanBuilder();
if (StringUtils.hasText(titleKeyword)) {
condition.and(book.title.contains(titleKeyword));
}
if (categoryIds != null && !categoryIds.isEmpty()) {
condition.and(book.category.id.in(categoryIds));
}
if (from != null && to != null) {
condition.and(book.publishedOn.between(from, to));
}
List<Book> result = queryFactory.selectFrom(book).where(condition).fetch();
注意点は、BooleanBuilderが空のままwhere()に渡されたときの挙動です。条件が1つも積まれなければ全件取得になり、一覧画面でそのまま実行すると想定外の件数を返します。検索条件が未指定のときはlimit()を必ず併用するか、condition.hasValue()で判定して既定の条件を足しておくのが安全です。
DTO射影・集計・サブクエリの実装
エンティティ全体ではなく必要な列だけを取り出す射影、GROUP BYを伴う集計、相関のないサブクエリは、いずれもJPAQueryFactory経由でないと書けない領域です。以下のコードはいずれもQBook book = QBook.book;を宣言済みの前提で示します。
Projectionsと@QueryProjectionによるDTO射影
// DTO。@QueryProjection を付けると QBookSummary がビルド時に生成される
public class BookSummary {
private final String title;
private final int price;
@QueryProjection
public BookSummary(String title, int price) {
this.title = title;
this.price = price;
}
}
// 1) Projections.constructor:引数の順序と型で解決する(DTO 側に手を入れない)
List<BookSummary> a = queryFactory
.select(Projections.constructor(BookSummary.class, book.title, book.price))
.from(book)
.fetch();
// 2) 生成された QBookSummary を使う
List<BookSummary> b = queryFactory
.select(new QBookSummary(book.title, book.price))
.from(book)
.fetch();
2つの方式の違いはコンパイル時の型チェックです。Projections.constructorは引数の順序と型が合っているかを実行時に解決するため、列の順番を入れ替えるとその場では気づけません。@QueryProjectionはDTO用のQクラスを生成するので、引数の不一致がコンパイルエラーになります。代償として、DTOがQueryDSLへ依存する点は許容が必要です。共有ライブラリのDTOには前者、アプリケーション内部のDTOには後者、という切り分けが扱いやすいでしょう。
groupByとhavingを使った集計
List<Tuple> stats = queryFactory
.select(book.category.name, book.count(), book.price.avg())
.from(book)
.groupBy(book.category.name)
.having(book.count().gt(3L))
.fetch();
for (Tuple row : stats) {
String category = row.get(book.category.name);
Long count = row.get(book.count());
}
複数の列を選択した結果はTupleで返ります。値の取り出しはrow.get(...)に選択時と同じ式を渡す方式で、内部では式をキーにしたマップを引いています。bookが同一インスタンスであればbook.count()も同じ式オブジェクトを返すため、上のように書き下しても確実に取得できます。集計結果をそのままDTOへ詰めたいなら、Projections.constructorの引数に集計式を渡す書き方も可能です。
JPAExpressionsによるサブクエリ
QBook sub = new QBook("sub");
List<Book> aboveAverage = queryFactory
.selectFrom(book)
.where(book.price.gt(
JPAExpressions.select(sub.price.avg()).from(sub)
))
.fetch();
サブクエリはJPAExpressionsの静的メソッドで組み立てます。外側と同じエンティティを参照する場合は、上のsubのように別名を明示したQクラスを新しく生成しないと、同一の別名が衝突してJPQLが壊れます。なおJPQLの仕様上、FROM句にサブクエリは書けません。Jakarta Persistence 3.2でも派生テーブルは追加されていない仕様です。Hibernate 6以降のHQLはベンダ拡張として派生テーブルを扱えますが、そこへ寄せると移植性を失います。JOIN対象をサブクエリにしたい要件が出た場合は、素のSQLをquerydsl-sqlで組むか、ビューを用意する側へ倒すのが素直です。
fetch系メソッドの使い分けとページング
List<Book> content = queryFactory
.selectFrom(book)
.where(condition)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
JPAQuery<Long> countQuery = queryFactory
.select(book.count())
.from(book)
.where(condition);
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
結果取得は、リストならfetch()、1件に定まるはずならfetchOne()(複数件あれば例外)、先頭1件だけでよいならfetchFirst()を使います。注意したいのは件数取得で、fetchCount()とfetchResults()はquerydsl-jpaのAbstractJPAQueryで非推奨になっています。JPQLはサブクエリからのSELECTを許さないため、汎用的なカウントクエリを組み立てられないのが理由です。GROUP BY要素が複数あるクエリやHAVING句を含むクエリでは、QueryDSLは警告ログを出したうえでfetch()の結果件数を数えるメモリ上のカウントへ退避します。javadocが「大きな結果セットでは深刻な性能上の代償を伴う」と書いているとおり、静かに全件ロードが走る危険があるということ。上のように件数用のクエリを別に組み立てるのが現行の書き方です。PageableExecutionUtils.getPageを使うと、件数クエリの実行が不要なページ(1ページに収まる場合など)では発行自体が省略されます。
JOINとN+1問題への対処
QueryDSLのJOINはjoin()、leftJoin()、innerJoin()に加えて、結合条件を明示するon()、関連エンティティを同時に読み込むfetchJoin()を組み合わせて表現します。
joinとfetchJoinの書き分け
QBook book = QBook.book;
QAuthor author = QAuthor.author;
QReview review = QReview.review;
// 1) 関連を検索条件にするだけなら join(フェッチはしない)
List<Book> byAuthor = queryFactory
.selectFrom(book)
.join(book.author, author)
.where(author.name.eq("夏目漱石"))
.fetch();
// 2) 関連も一緒に読み込んで N+1 を防ぐなら fetchJoin
List<Book> withAuthor = queryFactory
.selectFrom(book)
.join(book.author, author).fetchJoin()
.fetch();
// 3) 結合条件を足したい場合は leftJoin + on
List<Book> withHighReviews = queryFactory
.selectFrom(book)
.leftJoin(book.reviews, review).on(review.score.goe(4))
.fetch();
1つ目と2つ目の違いは、生成されるSQLではなく取得後の挙動にあります。fetchJoin()を付けない場合、関連エンティティは遅延読み込みのままです。取得したBookを1件ずつループしてgetAuthor()を呼ぶと、件数分のSELECTが飛びます。これがN+1問題です。関連を表示に使うならfetchJoin()を付けます。ビュー描画中にクエリが飛ぶ構成そのものを見直したい場合は、spring.jpa.open-in-viewとは?OSIVの既定値trueと起動時警告の消し方・falseの判断基準を先に確認してください。
Hibernate 6以降でのdistinctの扱い
一対多の関連にfetchJoin()を使うと、結合結果の行数だけ親エンティティが重複します。Hibernate 5まではこれをdistinct()で除去するのが定石で、SQLに無駄なDISTINCTを出さないためのHINT_PASS_DISTINCT_THROUGHというヒントまで用意されていました。Hibernate 6でこの前提が変わります。公式の移行ガイドは、子コレクションをjoin fetchする際に親エンティティの重複を除くためのdistinctはもはや不要であり、重複は常にHibernateが除去すると明記しました。同時にHINT_PASS_DISTINCT_THROUGHは削除され、distinctは常にSQLへそのまま渡されるようになっています。
したがってSpring Boot 3以降(Hibernate 6以上)でdistinct()を書き続けると、重複除去には寄与せず、データベース側にソートと重複判定の負荷だけを課します。Hibernate 5時代のサンプルコードを写しているなら、この1行は外してください。もうひとつ、一対多のfetch joinとページングの併用は、Hibernateが全件をメモリ上で処理するため別の問題を招きます。一覧のページングが必要なら、IDだけを先に取得してから本体を引く2段階のクエリに分けます。
よくある質問
QueryDSLの最新バージョンはどれですか?
本家com.querydslはMaven Central上で5.1.0(2024年1月29日)が最新です。OpenFeign配下のフォークio.github.openfeign.querydslは7.5(2026年7月21日)が最新で、こちらは更新が続いています。Spring Bootを使っているなら、BOMが管理する5.1.0がそのまま既定値になります。
ElasticsearchのQuery DSLとQueryDSLは同じものですか?
別物です。ElasticsearchのQuery DSLは検索条件をJSONで記述する言語仕様で、この記事のQueryDSLはJavaのクエリ構築ライブラリを指します。検索結果には両方が混在するため、記事を参照するときはgroupIdやimport文で見分けてください。
Spring Boot 3に上げたらQueryDSLが動かなくなったのはなぜですか?
依存関係にjakarta分類子が付いているかを最初に確認してください。Spring Boot 3はJakarta EEへ移行しているため、分類子なしのquerydsl-jpaやquerydsl-aptはjavax.persistenceを参照し、エンティティを認識できません。両方に分類子を付け、cleanしてから再ビルドします。
Mavenでバージョンを省略するとエラーになるのはなぜですか?
Mavenの依存関係管理は分類子まで含めたキーで照合するのに対し、querydsl-bomには分類子付きのエントリが登録されていないためです。jakarta分類子を使う場合は<version>を明記してください。<properties>にバージョンを1か所定義しておくと、依存関係とアノテーションプロセッサの指定がずれません。
QuerydslPredicateExecutorだけで足りますか?
エンティティ全体を条件で絞り込むだけなら足ります。必要な列だけをDTOへ詰める射影、GROUP BYを伴う集計、fetch joinの明示的な制御が必要になった時点で、JPAQueryFactoryを使ったカスタムリポジトリが要ります。両方を同じリポジトリに継承させ、クエリの性質で使い分けるのが現実的な落としどころです。