Spring Data JPA@Query

概要

Spring Dataは、実行できるクエリを定義するための多くの方法を提供します。 これらのうちの1つは@Query注釈です。

このチュートリアルでは、Spring Data JPAで@Query注釈を使用して、JPQLクエリとネイティブSQLクエリの両方を実行する方法を説明します。

@Queryアノテーションでは不十分な場合に動的クエリを作成する方法も示します。

:

Spring Data JPAリポジトリの派生クエリメソッド

Spring Data JPAのクエリ派生メカニズムを調べます。
続きを読む→

Spring Data JPA@Modifying Annotation

@Queryと@Modifying annotationsを組み合わせて、Spring Data JPAでDMLおよびDDLクエリを作成します
続きを読む→

Select Query

Spring Data repositoryメソッドに対して実行するSQLを定義するには、@Queryアノテーションでメソッドに注釈を付けることができます。value属性には、実行するJPQLまたはSQLが含まれています。

@Query注釈は、@NamedQuery注釈またはormで定義された名前付きクエリよりも優先されます。xmlファイル。

名前付きクエリとしてドメインモデル内ではなく、リポジトリ内のメソッドのすぐ上にクエリ定義を配置するのは良いアプローチです。 リポジトリは永続性を担当するため、これらの定義を保存する方が良い場所です。

2.1. JPQL

デフォルトでは、クエリ定義はJPQLを使用します。

データベースからアクティブなユーザーエンティティを返す単純なリポジトリメソッドを見てみましょう:

@Query("SELECT u FROM User u WHERE u.status = 1")Collection<User> findAllActiveUsers();

2.2. Native

ネイティブSQLを使用してクエリを定義することもできます。 必要なのは、nativeQuery属性の値をtrueに設定し、注釈のvalue属性にネイティブSQLクエリを定義することだけです:

@Query( value = "SELECT * FROM USERS u WHERE u.status = 1", nativeQuery = true)Collection<User> findAllActiveUsersNative();

クエリで順序を定義する

@Queryアノテーションを持つSpringデータメソッド宣言にSort型の追加パラメータを渡すことができます。 これは、データベースに渡されるORDER BY句に変換されます。

3.1. JPA提供および派生メソッドのソート

findall(Sort)やメソッドシグネチャを解析することによって生成されるメソッドの場合、オブジェクトプロパティを使用してソートを定義することしかできません:

userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

ここで、nameプロパティの長さで並べ替えたいとします:

userRepository.findAll(Sort.by("LENGTH(name)"));

上記のコードを実行すると、例外が発生します:

org。スプリングフレームワーク。データ。マッピング。PropertyReferenceException:タイプUserのプロパティの長さ(名前)が見つかりません!

3.2. Jpql

クエリ定義にJPQLを使用すると、Spring Dataは問題なくソートを処理できます—Sort型のメソッドパラメータを追加するだけです:

@Query(value = "SELECT u FROM User u")List<User> findAllUsers(Sort sort);

このメソッドを呼び出して、Userオブジェクトのnameプロパティで結果を並べ替えるSortパラメータを渡すことができます:

userRepository.findAllUsers(Sort.by("name"));

また、@Queryアノテーションを使用したため、同じメソッドを使用して、名前の長さでソートされたユーザーのリストを取得できます:

userRepository.findAllUsers(JpaSort.unsafe("LENGTH(name)"));

JpaSortを使用することが重要です。unsafe()は、Sortオブジェクトインスタンスを作成します。

を使用する場合:

Sort.by("LENGTH(name)");

その後、上記のfindAll()メソッドで見たのとまったく同じ例外が表示されます。

Spring Dataが@Query注釈を使用するメソッドの安全でない並べ替え順序を検出すると、クエリにsort句を追加するだけです。sort byプロパティがドメインモデルに属しているかどうかのチェックをスキップします。

3.3. Native

@QueryアノテーションがネイティブSQLを使用する場合、ソートを定義することはできません。

そうすると、例外が発生します:

org。スプリングフレームワーク。データ。jpa。リポジトリー…クエリ。I n v a lidjpaquerymethodexception:動的ソートおよび/またはページネーション

でネイティブクエリを使用できません例外として、ソートはネイティブクエリではサポートされていません。 エラーメッセージには、ページネーションが例外を引き起こすヒントが表示されます。

しかし、ページネーションを有効にする回避策があり、次のセクションでそれをカバーします。

ページネーション

ページネーションを使用すると、ページ内の結果全体のサブセットだけを返すことができます。 これは、たとえば、webページ上の複数のページのデータをナビゲートするときに便利です。

ページネーションのもう一つの利点は、サーバーからクライアントに送信されるデータの量が最小限に抑えられることです。 より小さなデータを送信することで、一般的にパフォーマンスの向上を見ることができます。

4.1. JPQL

JPQLクエリ定義でページネーションを使用するのは簡単です:

@Query(value = "SELECT u FROM User u ORDER BY id")Page<User> findAllUsersWithPagination(Pageable pageable);

PageRequestパラメータを渡して、データのページを取得できます。

ページネーションはネイティブクエリでもサポートされていますが、少し追加の作業が必要です。

4.2. Native

追加の属性countQueryを宣言することで、ネイティブクエリのページネーションを有効にすることができます。

これは、結果全体の行数をカウントするために実行するSQLを定義します:

@Query( value = "SELECT * FROM Users ORDER BY id", countQuery = "SELECT count(*) FROM Users", nativeQuery = true)Page<User> findAllUsersWithPagination(Pageable pageable);

4.3. 2.0.4より前のSpring Data JPAバージョン

ネイティブクエリの上記のソリューションは、Spring Data JPAバージョン2.0.4以降で正常に動作します。

それ以前のバージョンでは、このようなクエリを実行しようとすると、並べ替えの前のセクションで説明したのと同じ例外が表示されます。

クエリ内にページネーションのための追加のパラメータを追加することで、これを克服することができます:

@Query( value = "SELECT * FROM Users ORDER BY id \n-- #pageable\n", countQuery = "SELECT count(*) FROM Users", nativeQuery = true)Page<User> findAllUsersWithPagination(Pageable pageable);

上記の例では、ページネーションパラメータのプレースホルダーとして”\n–#pageable\n”を追加します。 これは、クエリを解析し、pageableパラメータを注入する方法をSpring Data JPAに指示します。 この解決策は、H2データベースで機能します。

JPQLとネイティブSQLを使用して単純なselectクエリを作成する方法を説明しました。 次に、追加のパラメータを定義する方法を示します。

インデックス付きクエリパラメータ

メソッドパラメータをクエリに渡す方法には、インデックス付きパラメータと名前付きパラメータがあります。

このセクションでは、インデックス付きパラメータについて説明します。

5.1. JPQL

JPQLのインデックス付きパラメータの場合、Spring Dataはメソッド宣言に表示されるのと同じ順序でメソッドパラメータをクエリに渡します:

@Query("SELECT u FROM User u WHERE u.status = ?1")User findUserByStatus(Integer status);@Query("SELECT u FROM User u WHERE u.status = ?1 and u.name = ?2")User findUserByStatusAndName(Integer status, String name);

上記のクエリでは、statusメソッドパラメータはインデックス1のクエリパラメータに割り当てられ、nameメソッドパラメータはインデックス2のクエリパラメータに割り当てられます。

5.2. ネイティブクエリのネイティブ

インデックス付きパラメータは、JPQLの場合とまったく同じように動作します:

@Query( value = "SELECT * FROM Users u WHERE u.status = ?1", nativeQuery = true)User findUserByStatusNative(Integer status);

次のセクションでは、別のアプローチ、つまり名前を介してパラメータを渡す方法を示します。

名前付きパラメータ

名前付きパラメータを使用してメソッドパラメータをクエリに渡すこともできます。 これらは、repositoryメソッド宣言内の@Paramアノテーションを使用して定義します。

@Paramで注釈を付けられた各パラメータには、対応するJPQLまたはSQLクエリパラメータ名と一致する値文字列が必要です。 名前付きパラメーターを持つクエリは読みやすく、クエリをリファクタリングする必要がある場合にエラーが発生しにくくなります。

6.1. JPQL

前述のように、メソッド宣言で@Paramアノテーションを使用して、JPQLのnameで定義されたパラメータとメソッド宣言のパラメータを一致させます:

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")User findUserByStatusAndNameNamedParams( @Param("status") Integer status, @Param("name") String name);

上記の例では、SQLクエリとメソッドパラメータが同じ名前を持つように定義しましたが、値文字列が同じであれば必須ではありません:

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")User findUserByUserStatusAndUserName(@Param("status") Integer userStatus, @Param("name") String userName);

6.2. Native

ネイティブクエリ定義の場合、JPQLと比較して、名前を介してクエリにパラメータを渡す方法に違いはありません。@Param注釈を使用します:

@Query(value = "SELECT * FROM Users u WHERE u.status = :status and u.name = :name", nativeQuery = true)User findUserByStatusAndNameNamedParamsNative( @Param("status") Integer status, @Param("name") String name);

7. コレクションパラメータ

JPQLまたはSQLクエリのwhere句にIN(またはNOT IN)キーワードが含まれている場合を考えてみましょう:

SELECT u FROM User u WHERE u.name IN :names

この場合、コレクションをパラメータとして受け取るクエリメソッドを定義できます:

@Query(value = "SELECT u FROM User u WHERE u.name IN :names")List<User> findUserByNameList(@Param("names") Collection<String> names);

パラメータはコレクションであるため、List、HashSetなどで使用できます。

次に、@Modifyingアノテーションを使用してデータを変更する方法を示します。

@Modifyingでクエリを更新

@Queryアノテーションを使用して、@Modifyingアノテーションをrepositoryメソッドに追加することで、データベースの状態を変更できます。

8.1. JPQL

データを変更するrepositoryメソッドには、selectクエリと比較して2つの違いがあります—@Modifying注釈があり、もちろん、JPQLクエリはselectの代わりにupdateを使用します:

@Modifying@Query("update User u set u.status = :status where u.name = :name")int updateUserSetStatusForName(@Param("status") Integer status, @Param("name") String name);

戻り値は、クエリの実行が更新した行の数を定義します。 インデックス付きパラメータと名前付きパラメータの両方を更新クエリ内で使用できます。

8.2. Native

ネイティブクエリでもデータベースの状態を変更することができます。 @Modifying注釈を追加するだけです:

@Modifying@Query(value = "update Users u set u.status = ? where u.name = ?", nativeQuery = true)int updateUserSetStatusForNameNative(Integer status, String name);

8.3. Insert

挿入操作を実行するには、INSERTはJPAインターフェイスの一部ではないため、@Modifyingを適用し、ネイティブクエリを使用する必要があります:

@Modifying@Query( value = "insert into Users (name, age, email, status) values (:name, :age, :email, :status)", nativeQuery = true)void insertUser(@Param("name") String name, @Param("age") Integer age, @Param("status") Integer status, @Param("email") String email);

動的クエリ

多くの場合、実行時にのみ値がわかっている条件またはデータセットに基づいてSQLステートメントを構築する必要があります。 そのような場合は、静的クエリを使用することはできません。

9.1. 動的クエリの例

たとえば、実行時に定義されたセットからemail1、email2、…、emailnのような電子メールを持つすべてのユーザーを選択する必要がある状況を想像してみ:

SELECT u FROM User u WHERE u.email LIKE '%email1%' or u.email LIKE '%email2%' ... or u.email LIKE '%emailn%'

セットは動的に構築されるため、コンパイル時に追加するLIKE句の数を知ることはできません。

この場合、静的なSQLステートメントを提供できないため、@Queryアノテーションを使用することはできません。

代わりに、カスタム複合リポジトリを実装することで、ベースのJpaRepository機能を拡張し、動的クエリを構築するための独自のロジックを提供できます。 これを行う方法を見てみましょう。

9.2. カスタムリポジトリとJPA Criteria API

幸いにも、Springはカスタムフラグメントインターフェイスを使用してベースリポジトリを拡張する方法を提供します。 その後、それらをリンクして複合リポジトリを作成できます。

まず、カスタムフラグメントインターフェイスを作成します:

public interface UserRepositoryCustom { List<User> findUserByEmails(Set<String> emails);}

そして、我々はそれを実装します:

public class UserRepositoryCustomImpl implements UserRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public List<User> findUserByEmails(Set<String> emails) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = cb.createQuery(User.class); Root<User> user = query.from(User.class); Path<String> emailPath = user.get("email"); List<Predicate> predicates = new ArrayList<>(); for (String email : emails) { predicates.add(cb.like(emailPath, email)); } query.select(user) .where(cb.or(predicates.toArray(new Predicate))); return entityManager.createQuery(query) .getResultList(); }}

上記のように、JPA Criteria APIを活用して動的クエリを構築しました。

また、クラス名にImpl接尾辞を含める必要があります。 SpringはUserRepositoryCustom実装をUserRepositoryCustomImplとして検索します。 フラグメント自体はリポジトリではないため、Springはこのメカニズムに依存してフラグメントの実装を見つけます。

9.3. 既存のリポジトリの拡張

セクション2からセクション7までのすべてのクエリメソッドがUserRepositoryにあることに注意してください。

それでは、UserRepositoryの新しいインターフェイスを拡張してfragmentを統合します:

public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom { // query methods from section 2 - section 7}

9.4. リポジトリ

を使用して、最後に動的クエリメソッドを呼び出すことができます:

Set<String> emails = new HashSet<>();// filling the set with any number of itemsuserRepository.findUserByEmails(emails);

コンポジットリポジトリを正常に作成し、カスタムメソッドを呼び出しました。

結論

この記事では、@Queryアノテーションを使用してSpring Data JPAリポジトリメソッドでクエリを定義するいくつかの方法について説明しました。

カスタムリポジトリを実装し、動的クエリを作成する方法も学びました。

いつものように、この記事で使用されている完全なコード例はGitHubで入手できます。

コメントを残す

メールアドレスが公開されることはありません。