Spring Data JPAの使い方メモ
最近になってJavaのSpring Bootを使ったWeb開発の勉強をはじめました。その中でSpring Boot JRAというデータベースを操作するためのライブラリが便利だったので使い方をまとめておきます。
依存関係
以下はビルドツールにgradleを使用する場合の例です。Spring InitiallizerでSpring Data Jpaにチェックを入れるとbuild.gradleのdependenciesに次の項目が追加されていると思います。
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' }
Spring Data JPAとは
そもそもSpring Data JPAとは、Repositoryクラスを便利に実装するためのライブラリです。JavaのWeb開発で理想とされるMVCモデルにおいて、メインのControllerクラスなどからデータベースを直接扱うことは好ましいとされておらず、そのために用いられるデータベース操作のためのクラスがRepositoryクラスです。別の言い方だとDAO(Data Access Object)と言ったりもします。これを自分で実装するとSQLのクエリの種類ごとに記述しないといけないのでやや手間がかかるのですが、Spring Data JPRを使うとこれがごく簡単に書けます。たとえばEmployeeというエンティティクラス(データベースの入れ物となるクラスで、データベースのレコード=一つのデータに相当します)があって、nameというフィールドで指定した文字列に完全一致するものを取得したいときは次のように書けます。
@Repository public interface EmployeeRepository extends JpaRepository<Employee, String>{ List<Employee> findByName(String name); }
@Autowired EmployeeRepository employeeRepository; @RequestMapping(value = "/{name}", method = RequestMethod.GET) public String findEmployeeByName(@PathVariable("name") String name) { return employeeRepository.findByName(name); }
基本的な書き方はドキュメントを見てもらえればわかるのですが、検索したいフィールド名の最初を大文字にして、findBy~などの特定のフォーマットに合わせてメソッドを定義することで、JPAが自動で実装してくれます。便宜上引数もnameとしていますが、実際には何でも構いません。
以下は個人的につまずいた点などをまとめたメモです。
クエリを自分で定義したい
メソッドの前に@Query
アノテーションを付けます。例えば上のfindByNameメソッドのクエリ部分を自分で記述するなら以下のように書きます。?1
はメソッドの引数を表し、例えばメソッドの引数を増やしたときは?2, ?3 ...
とすることでそれぞれアクセスできます。
@Query("select e from Employee e where e.name = ?1") public List<Employee> findByName(String name);
このクエリ文はJPQLといい、SQLのクエリ文とは異なることに注意です。上のクエリをSQL上で実行しても当然ですがエラーが出ます。SQL構文をそのまま使いたいときは、アノテーション内でnativeQuery=true
とします。
@Query(value = "SELECT * FROM Employees WHERE name LIKE ?1", nativeQuery=true) public List<Employee> findByName(String name);
部分一致検索をしたい
findBy(フィールド名)Containing
を使います。
public List<Employee> findByNameContaining(String name); @Query("select e from Employee e where e.name like %?1% ") public List<Employee> findByLikeName(String name);
特定の引数を省略したい
Spring Data JPAではメソッド名にANDを加えることで複数のフィールド(=カラム)を検索できます。
public List<Employee> findByNameAndStatusContaining(String name, String status);
このとき、特定の引数にnullが入るとうまく検索できなくなります。これを防ぐためには、Controller側でデフォルトの値をワイルドカードである%
に指定しておくといいと思います。以下の例ではdefaultValue = "%"
がそれにあたります。
@RequestMapping(value = "/", method = RequestMethod.GET) public String getEmployeesByQuery(@RequestParam(value = "name", required = false, defaultValue = "%") String name, @RequestParam(value = "status", required = false, defaultValue = "%") String status){ ~~~~ }
Ordinal parameter not bound エラーが出る
@Query
アノテーション内で、?1
を省略するとおそらく次のエラーが出ると思います。一番目の引数である?1
は必ず使用しなければならないようです。
There was an unexpected error (type=Internal Server Error, status=500). Ordinal parameter not bound : 5; nested exception is org.hibernate.QueryException: Ordinal parameter not bound : 5 org.springframework.dao.InvalidDataAccessResourceUsageException: Ordinal parameter not bound : 5; nested https://kaige.org/2019/07/10/org-hibernate-QueryException-Ordinal-parameter-not-bound/