データ]

  • リンク:/tag/jpa/[JPA]


1概要

Spring Dataは実行可能なクエリを定義するための多くの方法を提供します。

その1つが

@ Query

アノテーションです。

このチュートリアルでは、Spring Data JPAで

@ Query

アノテーションを使用してJPQLとネイティブSQLクエリの両方を実行する方法を説明します。


2クエリを選択

Spring Dataリポジトリメソッドに対して実行するSQLを定義するために、メソッドに

@ Query

アノテーションを付けることができます – その

value

属性には実行するJPQLまたはSQLが含まれます。


@ Query

アノテーションは、

@ NamedQuery

でアノテーションが付けられているか、

orm.xml

ファイルで定義されている名前付きクエリよりも優先されます。

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


2.1. JPQL

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

データベースからアクティブな

User

エンティティを返す簡単なリポジトリメソッドを見てみましょう。

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


2.2. ネイティブ

ネイティブSQLを使用してクエリを定義することもできます。あとは、

nativeQuery

属性の値を

true

に設定し、アノテーションの

value

属性にネイティブSQLクエリを定義するだけです。

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


3クエリでの順序の定義


@ Query

アノテーションを持つSpring Dataメソッドの宣言に

__Sort


型の追加パラメータを渡すことができます。データベースに渡される

ORDER BY__句に変換されます。


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


findAll(Sort)

やメソッドシグネチャの解析によって生成されたものなど、すぐに使用できるメソッドでは、オブジェクトプロパティを使用してソートを定義することができます。

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

  • nameプロパティの長さでソートしたいとしましょう。**

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

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































































































































__ org.springframework.data.mapping.PropertyReferenceException:User型のプロパティlENGTH(name)が見つかりません。

====

3.2. JPQL

  • クエリ定義にJPQLを使用すると、Spring Dataはソートを問題なく処理できます** –

    Sort

    型のメソッドパラメータを追加するだけです。

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

このメソッドを呼び出して

Sort

パラメータを渡すと、

User

オブジェクトの

name

プロパティで結果が並べ替えられます。

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

また、

@ Query

アノテーションを使用したので、

同じメソッドを使用して、名前の長さで

Users

のソート済みリストを取得できます

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


  • Sort

    オブジェクトインスタンスを作成するには、

    JpaSort.unsafe()

    を使用することが重要です。**

使うとき:

new Sort("LENGTH(name)");

それから、

findAll()

メソッドについて上で見たのとまったく同じ例外を受け取ります。

Spring Dataが

@ Query

アノテーションを使用するメソッドの安全でない

Sort

順序を発見すると、クエリにsort句を追加するだけです – ソートするプロパティがドメインモデルに属しているかどうかのチェックをスキップします。

====

3.3. ネイティブ


  • @ Query

    アノテーションがネイティブSQLを使用している場合、

    Sort

    を定義することはできません。**

そうした場合、例外が発生します。

org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException

動的な並べ替えやページ付けを伴うネイティブクエリは使用できません。

例外が言うように、ソートはネイティブクエリではサポートされていません。エラーメッセージは、ページ付けが例外を引き起こすというヒントを私たちに与えます。

ただし、ページネーションを有効にするための回避策があります。次のセクションで説明します。

===

4ページ付け

ページ付けを使用すると、結果全体のサブセットのみを

Page

に返すことができます。これは、たとえば、Webページ上の複数ページのデータをナビゲートするときに役立ちます。

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

====

4.1. JPQL

JPQLクエリ定義でページ区切りを使用するのは簡単です。

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

データのページを取得するために

PageRequest

パラメータを渡すことができます。ページ区切りはネイティブクエリでもサポートされていますが、少し追加の作業が必要です。

====

4.2. ネイティブ

追加の属性

countQuery

** を宣言することで、ネイティブクエリのページネーションを有効にすることができます。これは、結果全体の行数を数えるために実行するSQLを定義します。

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

====

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

ネイティブクエリに対する上記の解決策は、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」を追加します。これはSpring Data JPAにクエリの解析方法とページング可能パラメータの挿入方法を指示します。この解決策は

H2

データベースに有効です。

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

===

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

メソッドパラメータをクエリに渡す方法は2つあります。このセクションでは、インデックス付きパラメータについて説明します。

====

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

次のセクションでは、名前を使ってパラメータを渡すという別のアプローチを紹介します。

===

6. 名前付きパラメータ

名前付きパラメータを使用して

メソッドパラメータをクエリに渡すこともできます

リポジトリメソッド宣言内の

@ Param

アノテーションを使用してこれらを定義します。


@ Param

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

====

6.1. JPQL

上記のように、JPQLでnameで定義されているパラメータとメソッド宣言のパラメータを照合するために、メソッド宣言で

@ Param

アノテーションを使用します。

@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. ネイティブ

ネイティブのクエリ定義では、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);

次に、@

Modifying

アノテーションを使用してデータを変更する方法を説明します。

===

7.

@ Modifying


を使用してクエリを更新する

@

Modifying

アノテーション** をリポジトリメソッドに追加することで、@

Query

アノテーションを使用してデータベースの状態を変更することもできます。

====

7.1. JPQL

データを修正するリポジトリメソッドは

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

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

====

7.2. ネイティブ

データベースの状態はネイティブのクエリでも変更できます – 単に

@ Modifying

アノテーションを追加する必要があります。

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

===

8結論

この記事では、

@ Query

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

いつものように、このチュートリアルで使用されている完全なコード例はhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa[Githubで入手可能]です。