JPQLとクエリメソッドの基礎を完全解説!Spring初心者でも理解できるデータ取得の基本
新人
「Springでデータベースから値を取り出すには、どうやってやるんですか?」
先輩
「Springでは、JPQLという言語を使って、エンティティからデータを取得できます。また、クエリメソッドを使うともっと簡単に書けることもありますよ。」
新人
「SQLとどう違うんですか?難しそうですね……」
先輩
「それじゃ、まずはJPQLの基本から順番に見ていこうか!」
1. JPQLとは何か?
JPQLとは「Java Persistence Query Language(ジャバ・パーシステンス・クエリ・ランゲージ)」の略で、Spring BootなどのJavaアプリケーションでエンティティ(データベースのテーブルに対応するクラス)を操作するためのクエリ言語です。
通常のSQLはテーブルを対象にしていますが、JPQLはJavaのEntityクラスを対象にします。つまり、テーブル名ではなく、Javaクラス名とフィールド名を使って記述します。
例えば、「名前が 'Taro' のユーザーを取得する」という処理をSQLで書くと次のようになります。
SELECT * FROM users WHERE name = 'Taro';
一方、JPQLでは次のようになります。
SELECT u FROM User u WHERE u.name = 'Taro'
このように、JPQLはテーブルやカラムではなく、クラス(エンティティ)とそのプロパティ(フィールド)を使ってクエリを記述するのが特徴です。
JPQLは、エンティティに対してオブジェクト指向的にクエリを投げることができるため、Springなどのフレームワークとの相性が非常に良くなっています。
2. JPQLを使うための準備
JPQLを使うには、まずデータベースと対応するEntityクラスを定義する必要があります。エンティティクラスは@Entityというアノテーションをつけて定義します。以下は、簡単なユーザー情報を管理するUserエンティティの例です。
package com.example.demo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
// GetterとSetterは省略
}
このUserクラスは、データベースのuserテーブルと自動的にマッピングされ、JPQLでこのクラスを対象にクエリを発行できるようになります。
次に、JPQLを実行するためのリポジトリクラスを用意します。これは、Spring Data JPAのJpaRepositoryを継承して作成します。
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
このようにして、UserRepositoryを通じて、JPQLを使ったデータ取得や保存が可能になります。次回は、実際にJPQLを使ってどのようにデータを取得するかを解説していきます。
3. JPQLの基本的な使い方
JPQLの文法は、基本的にSQLに似ていますが、テーブル名やカラム名の代わりにJavaのエンティティクラス名とプロパティ名を使う点が大きな違いです。JPQLでは、SELECTやWHEREなどのキーワードを使って、オブジェクト指向的にデータを取得することができます。
たとえば、名前が「Taro」のユーザー情報を取得するJPQL文は次のようになります。
SELECT u FROM User u WHERE u.name = 'Taro'
この文では、Userというエンティティにエイリアスuを付けて、u.nameが「Taro」と一致するデータを検索しています。
このようなJPQLをSpringで実行するには、@Queryアノテーションを使います。以下はその実装例です。
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.age > 20")
List<User> findUsersOlderThan20();
}
この例では、Userエンティティの中でageが20より大きいユーザーをすべて取得するクエリをJPQLで記述しています。JPQLでは数値や文字列の条件も使えるため、非常に柔軟な検索が可能です。
このように@Queryを使えば、JPQLを直接記述して複雑な検索条件を設定できます。
4. クエリメソッドとは何か
クエリメソッドとは、Spring Data JPAが提供する便利な機能で、メソッド名をもとに自動的にSQL文(またはJPQL)を生成してくれる仕組みです。
例えば、findByNameというメソッドをリポジトリインターフェースに書くだけで、nameというプロパティで検索するSQLが自動で生成されます。
実際の定義は次のようになります。
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
}
このメソッドを呼び出すと、Springが自動的にSELECT u FROM User u WHERE u.name = ?1というJPQLを実行してくれます。
クエリメソッドは、特定のルールに従って命名することで、比較演算子(GreaterThan、LessThanなど)や、文字列検索(Containing、Likeなど)もサポートしています。
以下にいくつかの例を示します。
List<User> findByAgeGreaterThan(int age);
List<User> findByNameContaining(String keyword);
List<User> findByAgeBetween(int start, int end);
このように、メソッド名だけで検索条件を指定できるのがクエリメソッドの大きな特徴であり、コードの可読性や保守性が高くなります。
5. クエリメソッドの作成と使用例
それでは実際に、クエリメソッドを使ってユーザー情報を検索し、@Controllerを使った画面表示まで行う例を見ていきましょう。
まず、リポジトリに次のようなクエリメソッドを定義します。
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
}
次に、コントローラクラスを作成し、画面に検索結果を渡す処理を実装します。
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@Controller
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/search")
public String search(Model model) {
List<User> users = userRepository.findByName("Taro");
model.addAttribute("users", users);
return "user-list";
}
}
このコントローラでは、/searchにアクセスすると、findByNameを使って「Taro」という名前のユーザーを検索し、結果をModelに詰めてビューへ渡しています。
最後に、表示するビュー(HTMLファイル)を作成します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ユーザー一覧</title>
</head>
<body>
<h1>検索結果</h1>
<ul>
<li th:each="user : ${users}">
名前:<span th:text="${user.name}"></span>/年齢:<span th:text="${user.age}"></span>
</li>
</ul>
</body>
</html>
このHTMLファイルでは、コントローラから渡されたユーザーリストを繰り返し処理して、名前と年齢を表示しています。
このように、クエリメソッドと@Controllerを組み合わせることで、Spring MVC構成でデータベース検索と画面表示を簡単に実装できます。
6. JPQLとクエリメソッドの使い分け
Springの開発では、JPQLとクエリメソッドのどちらもデータベース検索に利用できますが、それぞれ適した使い方があります。
クエリメソッドは、単純な検索条件であれば、非常に便利です。メソッド名を指定するだけで、自動的にJPQLを生成してくれるため、コーディングの手間が少なく、保守もしやすくなります。
例えば、「名前が一致するユーザーを取得する」「年齢が20歳以上のユーザーを検索する」といった単純な条件は、クエリメソッドで十分です。
一方、JPQLは、複雑な検索条件や結合、グループ化、サブクエリなどが必要なときに使います。たとえば、「年齢が20歳以上のユーザーで、名前に 'a' が含まれているものを、年齢の降順で並べる」といったクエリは、クエリメソッドだけで表現するのは難しいため、JPQLを使って明示的に記述します。
開発の現場では、まずクエリメソッドで対応できるかを検討し、無理であればJPQLに切り替えるという判断がよく行われています。
7. 注意点とよくあるエラー
JPQLやクエリメソッドを使う際に、初心者がよくつまずくポイントがあります。ここでは、代表的なエラーと注意点を紹介します。
● JPQLでエンティティ名やプロパティ名を間違える
JPQLではSQLと違って、テーブル名やカラム名ではなく、エンティティのクラス名とフィールド名を使う必要があります。たとえば、Userクラスのnameプロパティに対してクエリを書くときは、必ずu.nameのようにフィールド名を使います。
もしu.usernameのように存在しないフィールド名を使うと、実行時に「org.hibernate.QueryException」などの例外が発生します。
● クエリメソッドの命名ミス
クエリメソッドでは、プロパティ名を正確に使う必要があります。たとえば、Userクラスのフィールドがageであるのに、findByAgesなどと書いてしまうと、Springはそのプロパティを見つけられずエラーになります。
正しいプロパティ名を使って、findByAgeやfindByAgeGreaterThanのように命名しましょう。
● 戻り値の型のミス
検索結果が1件以上返る場合は、List<User>のようにリストで受け取りますが、1件だけを取得したいときにOptional<User>やUserで定義しないと、型不一致のコンパイルエラーになります。
複数件ならリスト、1件だけならOptionalを使う、というルールを守ることが大切です。
8. 実際のアプリケーションでの活用例
ここでは、名前で検索できる簡単なユーザー検索機能を例に、Spring MVC構成での実装方法を紹介します。
まずは、HTML側で名前を入力できるフォームを用意します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ユーザー検索</title>
</head>
<body>
<h1>ユーザー検索</h1>
<form th:action="@{/search}" method="get">
名前:<input type="text" name="name">
<button type="submit">検索</button>
</form>
<ul>
<li th:each="user : ${users}">
名前:<span th:text="${user.name}"></span> / 年齢:<span th:text="${user.age}"></span>
</li>
</ul>
</body>
</html>
次に、@Controllerを使って、名前を受け取って検索処理を行うコントローラを作成します。
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/search")
public String search(@RequestParam(name = "name", required = false) String name, Model model) {
List<User> users;
if (name != null && !name.isEmpty()) {
users = userRepository.findByNameContaining(name);
} else {
users = userRepository.findAll();
}
model.addAttribute("users", users);
return "user-search";
}
}
最後に、リポジトリに「名前を部分一致で検索するクエリメソッド」を追加します。
List<User> findByNameContaining(String keyword);
このように、findByNameContainingを使うことで、例えば「ar」と入力したときに「Taro」や「Karen」など部分一致した名前が取得されます。
Spring MVCの構成では、クエリメソッドとHTMLフォームを組み合わせることで、簡単に動的な検索機能を実装できます。
この検索機能は、ユーザー一覧、商品検索、ブログ記事検索などさまざまな場面で活用できます。JPQLとクエリメソッドを適切に使い分けることで、Springアプリケーションの柔軟なデータ取得が実現できます。