1. 序章
これは、データの永続性のための強力な QuerydslAPIを使用して稼働させるための入門記事です。
ここでの目標は、Querydslをプロジェクトに追加し、生成されたクラスの構造と目的を理解し、最も一般的なシナリオでタイプセーフなデータベースクエリを作成する方法の基本を理解するための実用的なツールを提供することです。
2. Querydslの目的
オブジェクトリレーショナルマッピングフレームワークは、EnterpriseJavaの中核です。 これらは、オブジェクト指向アプローチとリレーショナルデータベースモデルの間の不一致を補います。 また、開発者は、よりクリーンで簡潔な永続化コードとドメインロジックを記述できます。
ただし、ORMフレームワークの最も難しい設計上の選択の1つは、正しくタイプセーフなクエリを構築するためのAPIです。
最も広く使用されているJavaORMフレームワークの1つであるHibernate(および密接に関連するJPA標準)は、SQLに非常によく似た文字列ベースのクエリ言語HQL(JPQL)を提案します。 このアプローチの明らかな欠点は、型の安全性の欠如と静的クエリチェックの欠如です。 また、より複雑な場合(たとえば、条件に応じて実行時にクエリを作成する必要がある場合)、HQLクエリの作成には通常、文字列の連結が含まれますが、これは通常、非常に安全ではなく、エラーが発生しやすくなります。
JPA 2.0標準は、 Criteria Query API の形式で改善をもたらしました。これは、アノテーションの前処理中に生成されたメタモデルクラスを利用したクエリを構築する新しいタイプセーフな方法です。 残念ながら、本質的に画期的であるため、Criteria Query APIは非常に冗長で、実際には判読できませんでした。 SELECT p FROM Petpのような単純なクエリを生成するためのJakartaEEチュートリアルの例を次に示します。
EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
TypedQuery<Pet> q = em.createQuery(cq);
List<Pet> allPets = q.getResultList();
生成されたメタデータクラスの同じアイデアに基づいて、より適切な Querydsl ライブラリがすぐに登場したのも不思議ではありませんが、流暢で読みやすいAPIで実装されています。
3. Querydslクラスの生成
Querydslの流暢なAPIを説明する魔法のメタクラスを生成して探索することから始めましょう。
3.1. QuerydslをMavenビルドに追加する
プロジェクトにQuerydslを含めるのは、ビルドファイルにいくつかの依存関係を追加し、JPAアノテーションを処理するためのプラグインを構成するのと同じくらい簡単です。 依存関係から始めましょう。 Querydslライブラリのバージョンは、内部の別のプロパティに抽出する必要がありますセクションは次のとおりです(Querydslライブラリの最新バージョンについては、 Maven Central リポジトリ):
<properties>
<querydsl.version>4.1.3</querydsl.version>
</properties>
次に、次の依存関係をに追加しますあなたのセクション pom.xml ファイル:
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
querydsl-apt 依存関係は、注釈処理ツール(APT)—対応するJava APIの実装であり、コンパイル段階に進む前にソースファイル内の注釈を処理できます。 このツールは、いわゆるQタイプを生成します。これは、アプリケーションのエンティティクラスに直接関連するクラスですが、先頭に文字Qが付いています。 たとえば、アプリケーションに@Entityアノテーションが付けられたUserクラスがある場合、生成されたQタイプはQUser.javaに存在します。ソースファイル。
querydsl-apt依存関係のprovidedスコープは、このjarをビルド時にのみ使用可能にする必要があり、アプリケーションアーティファクトには含めないことを意味します。
querydsl-jpaライブラリはQuerydsl自体であり、JPAアプリケーションと一緒に使用するように設計されています。
を利用する注釈処理プラグインを構成するには querydsl-apt 、次のプラグイン構成をpomに追加します– エレメント:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
このプラグインは、Mavenビルドのプロセス目標中にQタイプが生成されることを確認します。 outputDirectory 構成プロパティは、Qタイプのソースファイルが生成されるディレクトリを指します。 このプロパティの値は、後でQファイルを調べるときに役立ちます。
IDEがこれを自動的に行わない場合は、このディレクトリをプロジェクトのソースフォルダにも追加する必要があります。これを行う方法については、お気に入りのIDEのドキュメントを参照してください。
この記事では、ブログサービスの単純なJPAモデルを使用します。これは、ユーザーとその BlogPosts で構成され、それらの間には1対多の関係があります。
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String login;
private Boolean disabled;
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "user")
private Set<BlogPost> blogPosts = new HashSet<>(0);
// getters and setters
}
@Entity
public class BlogPost {
@Id
@GeneratedValue
private Long id;
private String title;
private String body;
@ManyToOne
private User user;
// getters and setters
}
モデルのQタイプを生成するには、次のコマンドを実行します。
mvn compile
3.2. 生成されたクラスの調査
次に、apt-maven-pluginの outputDirectory プロパティで指定されたディレクトリ(この例では target /generated-sources / java )に移動します。 すべてのクラスが文字Q(この場合はQUserおよびQBlogPost)で始まることを除いて、ドメインモデルを直接反映するパッケージとクラス構造が表示されます。
ファイルQUser.javaを開きます。 これは、ルートエンティティとしてUserを持つすべてのクエリを構築するためのエントリポイントです。 最初に気付くのは、 @Generated アノテーションです。これは、このファイルが自動的に生成されたため、手動で編集しないことを意味します。 ドメインモデルクラスのいずれかを変更した場合は、 mvn compile を再度実行して、対応するすべてのQタイプを再生成する必要があります。
このファイルに存在するいくつかのQUserコンストラクターの他に、QUserクラスのパブリック静的最終インスタンスにも注意する必要があります。
public static final QUser user = new QUser("user");
これは、このエンティティに対するほとんどのQuerydslクエリで使用できるインスタンスです。ただし、単一のクエリでテーブルの複数の異なるインスタンスを結合するなど、より複雑なクエリを作成する必要がある場合を除きます。
最後に注意する必要があるのは、エンティティクラスのすべてのフィールドに、 NumberPath id 、StringPathログインなどの対応する*PathフィールドがQタイプにあることです。 QUserクラスのおよびSetPathblogPosts ( Set に対応するフィールドの名前が複数形になっていることに注意してください)。 これらのフィールドは、後で遭遇する流暢なクエリAPIの一部として使用されます。
4. Querydslを使用したクエリ
4.1. 単純なクエリとフィルタリング
クエリを作成するには、最初に JPAQueryFactory のインスタンスが必要です。これは、作成プロセスを開始するための推奨される方法です。 JPAQueryFactory に必要なのは、 EntityManager だけです。これは、 EntityManagerFactory.createEntityManager()呼び出しまたは@PersistenceContextを介してJPAアプリケーションですでに使用可能になっているはずです。 インジェクション。
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("com.baeldung.querydsl.intro");
EntityManager em = entityManagerFactory.createEntityManager();
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
次に、最初のクエリを作成しましょう。
QUser user = QUser.user;
User c = queryFactory.selectFrom(user)
.where(user.login.eq("David"))
.fetchOne();
ローカル変数QUserユーザーを定義し、QUser.user静的インスタンスで初期化したことに注意してください。 これは簡潔にするために純粋に行われます。あるいは、静的QUser.userフィールドをインポートすることもできます。
JPAQueryFactoryのselectFromメソッドがクエリの作成を開始します。 これにQUserインスタンスを渡し、 .where()メソッドを使用してクエリの条件節を作成し続けます。 user.login は、前に見たQUserクラスのStringPathフィールドへの参照です。 StringPath オブジェクトには、 .eq()メソッドもあり、フィールドの等価条件を指定することで、クエリの作成をスムーズに続行できます。
最後に、データベースから永続コンテキストに値をフェッチするために、 fetchOne()メソッドの呼び出しで構築チェーンを終了します。 このメソッドは、オブジェクトが見つからない場合は null を返しますが、 .where()条件を満たす複数のエンティティがある場合はNonUniqueResultExceptionをスローします。
4.2. 注文とグループ化
次に、リスト内のすべてのユーザーを、ログイン順に昇順で並べ替えて取得しましょう。
List<User> c = queryFactory.selectFrom(user)
.orderBy(user.login.asc())
.fetch();
この構文が可能なのは、 * Pathクラスに.asc()および .desc()メソッドがあるためです。 .orderBy()メソッドに複数の引数を指定して、複数のフィールドで並べ替えることもできます。
では、もっと難しいことを試してみましょう。 すべての投稿をタイトルでグループ化し、重複するタイトルを数える必要があるとします。 これは、 .groupBy()句を使用して行われます。 また、結果の発生回数でタイトルを並べ替えます。
NumberPath<Long> count = Expressions.numberPath(Long.class, "c");
List<Tuple> userTitleCounts = queryFactory.select(
blogPost.title, blogPost.id.count().as(count))
.from(blogPost)
.groupBy(blogPost.title)
.orderBy(count.desc())
.fetch();
ブログ投稿のタイトルと重複の数を選択し、タイトルでグループ化してから、集計された数で並べ替えました。 .orderBy()で参照する必要があるため、最初に。 select()句の count()フィールドのエイリアスを作成したことに注意してください。句。
4.3. 結合とサブクエリを含む複雑なクエリ
「HelloWorld!」というタイトルの投稿を書いたすべてのユーザーを見つけましょう。 このようなクエリには、内部結合を使用できます。 .on()句で参照するために、結合されたテーブルのエイリアスblogPostを作成したことに注意してください。
QBlogPost blogPost = QBlogPost.blogPost;
List<User> users = queryFactory.selectFrom(user)
.innerJoin(user.blogPosts, blogPost)
.on(blogPost.title.eq("Hello World!"))
.fetch();
次に、サブクエリで同じことを実現してみましょう。
List<User> users = queryFactory.selectFrom(user)
.where(user.id.in(
JPAExpressions.select(blogPost.user.id)
.from(blogPost)
.where(blogPost.title.eq("Hello World!"))))
.fetch();
ご覧のとおり、サブクエリはクエリと非常によく似ており、非常に読みやすくなっていますが、JPAExpressionsファクトリメソッドで始まります。 サブクエリをメインクエリに接続するために、いつものように、以前に定義および使用されたエイリアスを参照します。
4.4. データの変更
JPAQueryFactory を使用すると、クエリを作成できるだけでなく、レコードを変更および削除することもできます。 ユーザーのログインを変更して、アカウントを無効にしましょう。
queryFactory.update(user)
.where(user.login.eq("Ash"))
.set(user.login, "Ash2")
.set(user.disabled, true)
.execute();
さまざまなフィールドに必要な.set()句をいくつでも含めることができます。 .where()句は不要なので、すべてのレコードを一度に更新できます。
特定の条件に一致するレコードを削除するには、同様の構文を使用できます。
queryFactory.delete(user)
.where(user.login.eq("David"))
.execute();
.where()句も必要ありませんが、 .where()句を省略すると、特定のタイプのエンティティがすべて削除されるため、注意が必要です。
JPAQueryFactoryに.insert()メソッドがないのはなぜか疑問に思われるかもしれません。 これは、JPAQueryインターフェースの制限です。 基礎となるjavax.persistence.Query.executeUpdate()メソッドは、更新および削除を実行できますが、ステートメントを挿入することはできません。 データを挿入するには、EntityManagerを使用してエンティティを永続化する必要があります。
データを挿入するために同様のQuerydsl構文を引き続き利用する場合は、querydsl-sqlライブラリにあるSQLQueryFactoryクラスを使用する必要があります。
5. 結論
この記事では、Querydslによって提供される永続的なオブジェクト操作のための強力でタイプセーフなAPIを発見しました。
Querydslをプロジェクトに追加する方法を学び、生成されたQタイプを調査しました。 また、いくつかの典型的なユースケースを取り上げ、それらの簡潔さと読みやすさを楽しんだ。
例のすべてのソースコードは、githubリポジトリにあります。
最後に、もちろん、Querydslが提供する機能には、生のSQL、非永続コレクション、NoSQLデータベース、全文検索など、さらに多くの機能があります。これらの機能のいくつかについては、今後の記事で説明します。