SpringのJPQLと@Queryの使い方を完全ガイド!ネイティブSQLとの違いも解説
生徒
「SpringでSQLっぽいことをしたいときって、どうやって書けばいいんですか?」
先生
「Spring Data JPAでは、JPQLというクエリ言語や@Queryアノテーションを使って柔軟にデータを取得できますよ。」
生徒
「SQLとは違うんですか?ネイティブSQLとの違いも知りたいです!」
先生
「では、JPQLとネイティブSQLの違い、@Queryの使い方、使い分けについて一緒に見ていきましょう。」
1. JPQLとは?SQLとの違いを解説
JPQL(Java Persistence Query Language)は、Javaのエンティティクラスを操作するためのクエリ言語です。SQLと構文は似ていますが、テーブル名ではなくエンティティ名やフィールド名を使います。
たとえば、ユーザーをID順に取得するJPQLは以下のようになります。
@Query("SELECT u FROM User u ORDER BY u.id ASC")
List<User> findAllUsers();
このように、Userというのはテーブルではなくエンティティ名を指しています。u.idもカラムではなく、エンティティのプロパティ名です。
2. @QueryアノテーションでJPQLを使う方法
Spring Data JPAでは、@Queryアノテーションを使うことで、メソッド名に依存しないクエリを自由に記述できます。
例えば、「名前でユーザーを検索する」場合は以下のように記述します。
@Query("SELECT u FROM User u WHERE u.name = :name")
User findByName(@Param("name") String name);
:nameはパラメータで、@Paramで指定することで値をバインドできます。JPQLでは型安全なエンティティベースの記述ができ、リファクタにも強く、保守性も高いのが特長です。
3. ネイティブSQLの使い方と@QueryのnativeQuery属性
JPQLではできない複雑なSQLやデータベース固有の関数を使いたいときには、ネイティブSQL(生のSQL)を使うことができます。@QueryにnativeQuery = trueを指定することで可能です。
@Query(value = "SELECT * FROM users WHERE email = ?1", nativeQuery = true)
User findByEmail(String email);
この場合、テーブル名はusersのように実際のデータベースのスキーマ名を使用します。エイリアスもSQLの書き方と同様に使えます。
4. JPQLとネイティブSQLの使い分けポイント
JPQLとネイティブSQLは、使い分けが重要です。以下のような基準で選ぶと良いでしょう。
- JPQLを使う場面:基本的な検索・更新・削除、エンティティベースで完結する処理
- ネイティブSQLを使う場面:データベースの特有の構文、複雑な結合や関数、パフォーマンスチューニング
例えば、PostgreSQLのjsonbやMySQLのLIMITなどはJPQLでは扱いづらいため、ネイティブSQLが必要です。
5. JPQLでのJOIN文の書き方と注意点
JPQLでもJOINを使ってテーブル(エンティティ)間のリレーションを扱うことができます。
@Query("SELECT o FROM Order o JOIN o.customer c WHERE c.name = :name")
List<Order> findOrdersByCustomerName(@Param("name") String name);
このように、JOINではエンティティのプロパティをたどる形で記述します。SQLのようにテーブル名を直書きすることはありません。
6. DTOを使ったJPQLクエリの書き方
JPQLでは、DTO(データ転送オブジェクト)を使って必要なフィールドだけを取り出すこともできます。クエリ内でnewを使ってDTOを生成します。
@Query("SELECT new com.example.dto.UserDto(u.name, u.email) FROM User u")
List<UserDto> findUserDtos();
この形式は、パフォーマンスを意識した設計にも有効で、必要なカラムだけを選択してデータ転送量を最適化できます。
7. @Modifyingと@Queryの組み合わせで更新系JPQLを書く
更新系クエリ(UPDATE/DELETE)は@Modifyingアノテーションを併用して書きます。トランザクション制御も必要なので注意しましょう。
@Transactional
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
void updateUserStatus(@Param("id") Long id, @Param("status") String status);
@Transactionalをメソッドに付けることで、更新クエリがトランザクション内で安全に実行されます。
8. @Queryを使うときのよくあるエラーと対処法
JPQL/ネイティブSQLの@Queryでありがちなエラーには以下のようなものがあります。
- エンティティ名とテーブル名の混同(JPQLではエンティティ名)
- ネイティブSQLなのに
nativeQuery = trueを付け忘れ - 引数の名前と
:paramの不一致
こうしたエラーはIDEの補完やユニットテストで事前に気づくようにすると、開発効率が上がります。