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)で使用する。
@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など)のオブジェクトを取得して実装する。
Session session = entityManager.unwrap(Session.class);
:
Spring Data JPA のクエリー実装方法
Spring Data JPA では Repositoryインターフェイスにメソッドを定義することでクエリーが作成される。
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.repository.PagingAndSortingRepository
- org.springframework.data.jpa.repository.JpaSpecificationExecutor
JpaRepositoryには標準的なCRUD操作を行うメソッドが用意されている。
通常はJpaRepositoryだけ使用すれば良いが、後述のSpecificationを使用する場合はJpaSpecificationExecutorも併用する。
標準のメソッド以外のクエリーを作るには、Repositoryインターフェイスにクエリーメソッドを追加する。
クエリーメソッドの実装方法は以下の通り。
- 命名規約に従ったメソッド名での自動実装
-
@Query
アノテーションでのクエリー指定 - リポジトリ実装クラスでクエリーを実装する
- Specificationでの実装
命名規約に従ったメソッド名での自動実装
決められたパターンでメソッド名を定義すると、クエリーを指定しなくても自動実装される。
public List<User> findByNameContainsOrderByIdAsc(String name);
select u
from User u
where u.name like %:name%
order by u.id asc
@Query
アノテーションでのクエリー指定
複雑なクエリーや、命名規約ではメソッド名が長くなってしまう場合などには、@Query
アノテーションでJPQL又はネイティブSQLを指定して任意のメソッド名でクエリーを定義する事ができる。
@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クラスに定義した名前付きクエリーと結び付ける事ができる。
@Entity
@NamedQuery(name = "findUsers", query = "select u from User u where u.name like %:name% order by u.id asc")
public class User {
@Query(name = "findUsers")
public List<User> findByNamedQuery(@Param("name") String name);
が、せっかくRepositoryにクエリーをまとめてるのにEntityクラスにクエリーを書くという事はないので、Spring Data JPA で名前付きクエリーを使う際はプロパティファイルを使用する。
プロパティファイルに名前付きクエリーを定義する
クエリー名はデフォルトではEntity名.メソッド名
となるが、@Query
のname属性で変更可能。
User.findAllOrderByName=select u from User u order by u.name
User.findAllOrderByEmailDesc=select u from User u order by u.email desc
public List<User> findAllOrderByName();
@Query(name = "User.findAllOrderByEmailDesc")
public List<User> findAllOrderByEmail();
リポジトリ実装クラスでクエリーを実装する
動的クエリーの組み立てやJPAプロバイダの機能を使用する場合など、特殊なクエリーが必要な場合等には自分でクエリーメソッドを実装する事ができる。
public interface UserRepository extends
JpaRepository<User, Long>,
JpaSpecificationExecutor<User>,
UserRepositoryCustom {
public interface UserRepositoryCustom {
public List<User> findUsers(String name, String email);
}
public class UserRepositoryImpl implements UserRepositoryCustom {
@Autowired
EntityManager entityManager;
public List<User> findUsers(String name, String email) {
//ここにクエリーを実装する
}
}
Specification
Specificationは何らかの検索条件を表すインターフェイスで、CriteriaAPIを使用して検索条件を実装する。
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))
);