- 概要
- :
- Spring Data JPAリポジトリの派生クエリメソッド
- Spring Data JPA@Modifying Annotation
- Select Query
- 2.1. JPQL
- 2.2. Native
- クエリで順序を定義する
- 3.1. JPA提供および派生メソッドのソート
- 3.2. Jpql
- 3.3. Native
- ページネーション
- 4.1. JPQL
- 4.2. Native
- 4.3. 2.0.4より前のSpring Data JPAバージョン
- インデックス付きクエリパラメータ
- 5.1. JPQL
- 5.2. ネイティブクエリのネイティブ
- 名前付きパラメータ
- 6.1. JPQL
- 6.2. Native
- 7. コレクションパラメータ
- @Modifyingでクエリを更新
- 8.1. JPQL
- 8.2. Native
- 8.3. Insert
- 動的クエリ
- 9.1. 動的クエリの例
- 9.2. カスタムリポジトリとJPA Criteria API
- 9.3. 既存のリポジトリの拡張
- 9.4. リポジトリ
- 結論
概要
Spring Dataは、実行できるクエリを定義するための多くの方法を提供します。 これらのうちの1つは@Query注釈です。
このチュートリアルでは、Spring Data JPAで@Query注釈を使用して、JPQLクエリとネイティブSQLクエリの両方を実行する方法を説明します。
@Queryアノテーションでは不十分な場合に動的クエリを作成する方法も示します。
:
Spring Data JPAリポジトリの派生クエリメソッド
Spring Data JPA@Modifying Annotation
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で入手できます。