Edited at

Spring Data JPA でのクエリー実装方法まとめ

More than 3 years have passed since last update.

Spring Data JPA でクエリーを実装する方法をざっくりまとめてみた。


JPAのクエリー実装方法

先ずはJPAのクエリー実装方法を見てみる。

JPAでは EntityManage を使用してクエリーを構築/実行するが、主な実装方法は以下の通り。


  • ネイティブSQL

  • JPQL

  • CriteriaAPI

  • 名前付きクエリー

  • JPAプロバイダの機能を直接使用する


ネイティブSQL

EntityManager#createNativeQuery(String)を使用する。

        List<User> results = entityManager

.createNativeQuery("select * from user where name = :name", User.class)
.setParameter("name", "きい太")
.getResultList();


JPQL

EntityManager#createQuery(String)を使用する。

        List<User> results = entityManager

.createQuery("from User where name = :name", User.class)
.setParameter("name", "きい太")
.getResultList();


CriteriaAPI

EntityManager#createQuery(CriteriaQuery)を使用する。

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.where(cb.equal(root.get("name"), "きい太"));
List<User> results = entityManager
.createQuery(query)
.getResultList();


名前付きクエリー

@NamedQuery 又は @NamedNativeQuery を使用してEntityクラスに名前付きクエリーを定義しておく事ができる。

定義した名前付きクエリーはEntityManager#createNamedQuery(String)で使用する。


User.java

@Entity

@NamedQuery(name = "findByName", query = "from User where name = :name")
public class User {
}

        List<User> results = entityManager

.createNamedQuery("findByName", User.class)
.setParameter("name", "きい太")
.getResultList();


JPAプロバイダの機能を直接使用する

EntityManager#unwrap()でJPAプロバイダ(Hibernate,EclipseLinkeなど)のオブジェクトを取得して実装する。


Hibernateの場合

        Session session = entityManager.unwrap(Session.class);

:


Spring Data JPA のクエリー実装方法

Spring Data JPA では Repositoryインターフェイスにメソッドを定義することでクエリーが作成される。


UserRepository.java

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {

public User findByName(String name);
public User findByEmail(String email);
:

Repositoryのスーパーインターフェイスには以下が用意されている。


  • org.springframework.data.repository.CrudRepository


    • org.springframework.data.repository.PagingAndSortingRepository


      • org.springframework.data.jpa.repository.JpaRepository





  • org.springframework.data.jpa.repository.JpaSpecificationExecutor

JpaRepositoryには標準的なCRUD操作を行うメソッドが用意されている。

通常はJpaRepositoryだけ使用すれば良いが、後述のSpecificationを使用する場合はJpaSpecificationExecutorも併用する。

標準のメソッド以外のクエリーを作るには、Repositoryインターフェイスにクエリーメソッドを追加する。

クエリーメソッドの実装方法は以下の通り。


  • 命名規約に従ったメソッド名での自動実装


  • @Queryアノテーションでのクエリー指定

  • リポジトリ実装クラスでクエリーを実装する

  • Specificationでの実装


命名規約に従ったメソッド名での自動実装

決められたパターンでメソッド名を定義すると、クエリーを指定しなくても自動実装される。


UserRepository.java

    public List<User> findByNameContainsOrderByIdAsc(String name);



実行されるJPQL

select u

from User u
where u.name like %:name%
order by u.id asc


@Queryアノテーションでのクエリー指定

複雑なクエリーや、命名規約ではメソッド名が長くなってしまう場合などには、@QueryアノテーションでJPQL又はネイティブSQLを指定して任意のメソッド名でクエリーを定義する事ができる。


UserRepository.java

    @Query("select u from User u where u.name like %:name% order by u.id asc")

public List<User> findUsers(@Param("name") String name);


名前付きクエリーの指定

@Queryのname属性でEntityクラスに定義した名前付きクエリーと結び付ける事ができる。


User.java

@Entity

@NamedQuery(name = "findUsers", query = "select u from User u where u.name like %:name% order by u.id asc")
public class User {


UserRepository.java

    @Query(name = "findUsers")

public List<User> findByNamedQuery(@Param("name") String name);

が、せっかくRepositoryにクエリーをまとめてるのにEntityクラスにクエリーを書くという事はないので、Spring Data JPA で名前付きクエリーを使う際はプロパティファイルを使用する。


プロパティファイルに名前付きクエリーを定義する

クエリー名はデフォルトではEntity名.メソッド名となるが、@Queryのname属性で変更可能。


resources/META-INF/jpa-named-queries.properties

User.findAllOrderByName=select u from User u order by u.name

User.findAllOrderByEmailDesc=select u from User u order by u.email desc


UserRepository.java

    public List<User> findAllOrderByName();

@Query(name = "User.findAllOrderByEmailDesc")
public List<User> findAllOrderByEmail();


リポジトリ実装クラスでクエリーを実装する

動的クエリーの組み立てやJPAプロバイダの機能を使用する場合など、特殊なクエリーが必要な場合等には自分でクエリーメソッドを実装する事ができる。


UserRepository.java

public interface UserRepository extends 

JpaRepository<User, Long>,
JpaSpecificationExecutor<User>,
UserRepositoryCustom {


UserRepositoryCustom.java

public interface UserRepositoryCustom {

public List<User> findUsers(String name, String email);
}


UserRepositoryImpl.java

public class UserRepositoryImpl implements UserRepositoryCustom {

@Autowired
EntityManager entityManager;
public List<User> findUsers(String name, String email) {
//ここにクエリーを実装する
}
}


Specification

Specificationは何らかの検索条件を表すインターフェイスで、CriteriaAPIを使用して検索条件を実装する。


UserSpecifications.java

    public Specification<User> nameContains(String name) {

return StringUtils.isEmpty(name) ? null : (root, query, cb) -> {
return cb.like(root.get("name"), "%" + name + "%");
};
}
public Specification<User> emailContains(String email) {
return StringUtils.isEmpty(email) ? null : (root, query, cb) -> {
return cb.like(root.get("email"), "%" + email + "%");
};
}


呼び出す箇所

    List<User> results = userRepository.findAll(Specifications

.where(nameContains(name))
.and(emailContains(email))
);