Tagbangers Blog

Spring Data Jpaが提供するQuery Method機能を調べてみよう

Spring Data Jpaが提供する機能を調べてみよう


Query Methodの三つの機能

  • メソッド名前でクエリ作成
  • メソッド名前でJPA NamedQuery呼び出し
  • @Queryアノテーションを使いレポジトリインタフェースに直接クエリを定義

メソッド名前でクエリ作成

Spring Data JPAがメソッドの名前を分析してJPQLクエリを実行してくれる。

注意点:

エンティティの名前が変更されると必ずインタフェースに定義したメソッドの名前も一緒に変更する必要がある、そうでないとアプリケーションを立ち上げる時点でエラーが生じる。こうやってアプリケーションのロード時にエラーを検知できるのがSpring Data JPAのすごく大きい長所である。

純粋なJPA Repositoryの場合

  • ユーザの名前と歳(age)でユーザを検索する場合
 public List<User> findByUsernameAndAgeGreaterThan(String username, int age) {
     return em.createQuery("select u from User u where u.username = :username
 and m.age > :age")
             .setParameter("username", username)
             .setParameter("age", age)
             .getResultList();
 }

メソッド名前でクエリ生成機能の場合

  • Spring Data JPAはメソッドの名前を分析しJPQLを生成して実行してくれる。
 public interface UserRepository extends JpaRepository<User, Long> {
     List<User> findByUsernameAndAgeGreaterThan(String username, int age);
}

詳しくはこちらを参照https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html


Spring Data JPAが基本的に提供してくれるQuery Method機能

  • 照会find...By,read...By,query...By,get...By
    • ...にはクエリを説明する内容が入っても良い
  • カウントcount...By
    • 返り値のタイプ : long
  • EXISTSexists...By
    • 返り値のタイプ : boolean
  • 削除`delete...By`, `remove...By`
    • 返り値のタイプ : long
  • DISTINCT`findDistinct`, `findUserDistinctBy`
  • LIMIT`findFirst2`, `findFirst`, `findTop`, `findTop3`

JPA NamedQuery

JPAのNamedQueryを呼び出せる機能である。

まずNamedQueryを定義する

@Entity
@NamedQuery(
  name="User.findByUsername",
  query="select u from User u where u.username = :username"
)
public class Uesr {
...
}

JPAを直接使いNamedQueryを呼び出す方法

public class UserRepository {
  public List<User> findByUsername(String username) {
         ...
         List<User> resultList =
             em.createNamedQuery("User.findByUsername", User.class)
  }
}

Spring Data JPAでNamedQueryを呼び出す方法

@Query(name = "User.findByUsername")
List<User> findByUsername(@Param("username") String username);
  • @Query を省略してメソッドの名前だけでNamedQueryを呼び出すこともできる。
public interface UserRepository extends JpaRepository<User, Long> {
     List<User> findByUsername(@Param("username") String username);
 }
  • Spring Data JPAは定義したドメインクラス + .(period) + メソッド名前を組み合わせNamedQueryを探して実行してくれる。
  • もし実行するNamedQueryがないと上で説明した「**メソッド名前でクエリ作成**」戦略を使用する。

@Query, レポジトリメソッドにQueryを定義する

メソッドにJPQLクエリを作成

public interface UserRepository extends JpaRepository<User, Long> {
 @Query("select u from User u where u.username= :username and u.age = :age")
 List<User> findUser(@Param("username") String username, @Param("age") int age);
}
  • @Queryアノテーションをしよする
  • Spring Data JPAが提供するのか要確認
  • JPA NamedQueryみたいにアプリケーション実行時にSyntax Errorとかを検知できる。
  • すごく良い長所である。

値、DTO照会

単純照会

@Query(select u.username from User u)
List<User> findUsernameList();
  • JPAの @Embeddedがついたタイプもこの方式で照会できる。

DTOで直接照会

@Query("select new jp.co.tagbangers.UserDto(u.id, u.username, t.name) " +
         "from User u join u.team t")
List<UserDto> findUserDto();
  • DTOで直接照会するためにはJPAのnew命令語を使う必要がある。
  • コンストラクタが用意されている必要がある。
@Data
public class UserDto {


private Long id;
private String username;
private String teamName;


     public UserDto(Long id, String username, String teamName) {
         this.id = id;
         this.username = username;
         this.teamName = teamName;
     }
}

Parameter Binding

  • 順番方式
  • 名前方式
select m from Member m where m.username = ?0 // 順番
select m from Member m where m.username = :name // 名前

なるべく名前方式を使うのをお勧めする。順番をミスして入れると大変なバグになる可能性が高い。

import org.springframework.data.repository.query.Param


public interface UserRepository extends JpaRepository<User, Long> {


     @Query("select u from User u where u.username = :name")
     User findUser(@Param("name") String username);
}

Collection Parameter Binding

  • inをサポートする
@Query("select u from User u where u.username in :names")
List<User> findByNames(@Param("names") List<String> names);

変換タイプ

Spring Data JPAは柔軟な変換タイプをサポートする

List<User> findByUsername(String name); // Collection
  • 照会結果がない場合Empty Collectionを返す
User findByUsername(String name); // Select
  • 照会結果がない場合Nullを返す
  • 結果が2件以上だったら javax.persistence.NonUniqueResultException 例外を発生する。
Optional<User> findByUsername(String name); // Optional

詳しくはhttps://docs.spring.io/spring-data/jpa/reference/repositories/query-return-types-reference.html#page-title