1前書き

これは、データを永続化するための強力なhttp://www.querydsl.com/[Querydsl]APIを使用して運用を開始するための入門記事です。

ここでの目的は、プロジェクトにQuerydslを追加し、生成されたクラスの構造と目的を理解し、最も一般的なシナリオでタイプセーフなデータベースクエリを作成する方法の基本的な知識を得るための実用的なツールを提供することです。


2 Querydsl

の目的

オブジェクトリレーショナルマッピングフレームワークは、Enterprise Javaの中核です。

これらは、オブジェクト指向アプローチとリレーショナルデータベースモデルの不一致を補います。また、開発者はよりクリーンでより簡潔な持続性コードとドメインロジックを書くことができます。

ただし、ORMフレームワークにとって最も難しい設計上の選択の1つは、正確でタイプセーフなクエリを作成するためのAPIです。

最も広く使用されているJava ORMフレームワークの1つであるHibernate(および密接に関連したJPA標準)は、SQLと非常によく似たストリングベースの照会言語HQL(JPQL)を提案しています。このアプローチの明らかな欠点は、型安全性の欠如と静的問い合わせチェックの欠如です。また、より複雑な場合(たとえば、条件によっては実行時にクエリを構築する必要がある場合)、HQLクエリの構築には通常、文字列の連結が含まれます。これは通常、非常に危険でエラーが発生しやすいものです。

JPA 2.0規格では、https://docs.oracle.com/javaee/7/tutorial/persistence-criteria.htm#GJITV[Criteria Query API]という形式での改良が行われました。これは、タイプセーフなクエリー作成方法ですこれは、注釈の前処理中に生成されたメタモデルクラスを利用していました。残念ながら、本質的には画期的なので、Criteria Query APIは非常に冗長で実際には判読できませんでした。

これは、Java EEチュートリアルの、

SELECT p FROM Pet p

と同じくらい簡単なクエリを生成するための例です。

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();

より適切なhttp://www.querydsl.com[Querydsl]ライブラリが、生成されたメタデータクラスと同じアイデアに基づいてすぐに登場し、しかも流暢で読みやすいAPIで実装されているのも当然のことです。


3 Querydslクラス生成

Querydslの流暢なAPIを説明する魔法のメタクラスの生成と探索から始めましょう。


3.1. Maven Build

へのQuerydslの追加

プロジェクトにQuerydslを含めるのは、ビルドファイルに複数の依存関係を追加してJPAアノテーションを処理するためのプラグインを設定するのと同じくらい簡単です。依存関係から始めましょう。以下のように、Querydslライブラリのバージョンを

<project> <properties>

セクション内の別のプロパティに展開する必要があります(Querydslライブラリの最新バージョンについては、https://search.maven.org/classic/#を確認してください)。検索%7Cga%7C1%7Cg%3A%22com.querydsl%22[Maven Central]リポジトリ)

<properties>
    <querydsl.version>4.1.3</querydsl.version>
</properties>

次に、

pom.xml

ファイルの

<project> <dependencies>

セクションに次の依存関係を追加します。

<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が付いたクラスです。生成されたQタイプは

QUser.java

ソースファイルにあります。


querydsl-apt

依存関係の

provided

スコープは、このjarファイルがビルド時にのみ利用可能になり、アプリケーション成果物には含まれないようにする必要があることを意味します。

querydsl-jpaライブラリは、Querydslそれ自体であり、JPAアプリケーションと一緒に使用するように設計されています。


querydsl-apt

を利用する注釈処理プラグインを設定するには、

<project> <build> <plugins>

要素の内側に、以下のプラグイン設定を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>

このプラグインは、Q-typesがMavenビルドのプロセス目標の間に生成されることを確認します。

outputDirectory

設定プロパティは、Qタイプのソースファイルが生成されるディレクトリを指します。このプロパティの値は、後でQファイルを調べるときに役立ちます。

IDEがこれを自動的に行わない場合は、このディレクトリをプロジェクトのソースフォルダに追加する必要があります。その方法については、お気に入りのIDEのマニュアルを参照してください。

この記事では、

Users

とその

BlogPosts

を1対多の関係で構成した、ブログサービスの単純なJPAモデルを使用します。

@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

アノテーションです。これは、このファイルが自動的に生成されたため、手動で編集しないでください。ドメインモデルクラスのいずれかを変更した場合は、対応するすべてのQタイプを再生成するためにもう一度

mvn compile

を実行する必要があります。

このファイルに存在するいくつかの

QUser

コンストラクタとは別に、

QUser

クラスのpublic static final instanceにも注意してください。

public static final QUser user = new QUser("user");

これは、このエンティティに対するほとんどのQuerydslクエリで使用できるインスタンスです。ただし、1つのクエリに複数の異なるテーブルのインスタンスを結合するなど、より複雑なクエリを記述する必要がある場合は例外です。

最後に注意する必要があるのは、エンティティクラスのすべてのフィールドに対して、

QUser

クラスの

NumberPath id



StringPath login

、および

SetPath blogPosts

のように、Qタイプの対応する

** Path

フィールドがあるということです。

Set

に対応するフィールドは複数形になります。これらのフィールドは、後で遭遇する流暢なクエリAPIの一部として使用されます。


4 Querydsl

による問い合わせ


4.1. 簡単な問い合わせとフィルタリング

クエリを作成するには、まずhttp://www.querydsl.com/static/querydsl/4.1.3/apidocs/com/querydsl/jpa/impl/JPAQueryFactory.html[

JPAQueryFactory

]のインスタンスが必要です。構築プロセスを開始するための好ましい方法です。

JPAQueryFactory

が必要とする唯一のものは

EntityManager

です。これは、

EntityManagerFactory.createEntityManager()

callまたは

@ PersistenceContext

インジェクションを介してJPAアプリケーションですでに使用可能になっているはずです。

EntityManagerFactory emf =
  Persistence.createEntityManagerFactory("org.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

userを定義し、それを

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. 結合とサブクエリを含む複雑なクエリ

「Hello World!」というタイトルの投稿を書いたすべてのユーザーを見つけましょう。そのようなクエリには、内部結合を使用できます。

.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()

メソッドを持っていないのはなぜだろう。これはJPA Queryインタフェースの制限です。基礎となるhttp://docs.oracle.com/javaee/7/api/javax/persistence/Query.html#executeUpdate–[

javax.persistence.Query.executeUpdate()

]メソッドは更新と削除を実行できますが、文を挿入しません。データを挿入するには、エンティティをEntityManagerで永続化するだけです。

それでもデータを挿入するために同様のQuerydsl構文を利用したい場合は、http://www.querydsl.com/static/querydsl/4.1.3/apidocs/com/querydsl/sql/SQLQueryFactory.html[

SQLQueryFactory

を使用してください。]querydsl-sqlライブラリにあるクラス。


5結論

この記事では、Querydslによって提供される永続的なオブジェクト操作のための強力でタイプセーフなAPIを発見しました。

プロジェクトにQuerydslを追加することを学び、生成されたQタイプを調べました。また、いくつかの典型的なユースケースについても説明し、それらの簡潔さと読みやすさを楽しんでいます。

例のすべてのソースコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/querydsl[githubレポジトリ]にあります。

最後に、もちろん、生のSQL、非永続的なコレクション、NoSQLデータベース、全文検索など、Querydslが提供する機能は他にもたくさんあります。これらのいくつかについては今後の記事で説明します。