Spring Data JPAのRepositoryでクエリを自動生成するためのメソッド命名規約

Spring Data JPAで、 JpaRepository を継承したリポジトリインターフェースに対し、特定の命名規則でメソッドを宣言しておくと、 @Query アノテーションなど書かなくても処理を自動生成してくれる。

で、その命名規則、単純な検索処理であれば findAllByXxxIdAndYyyId みたいな感じでかけるのだが、引数でコレクションを渡して検索する場合とかを毎回忘れる。

忘れた回数を数えていたら、今年に入って10回目の忘却を達成してしまったのでメモ。

どこを見ればいいか

こちら: Spring Data JPA - Reference Documentation

Pleiades翻訳版はこちら: Spring Data JPA - リファレンスドキュメント

The JPA module supports defining a query manually as a String or having it being derived from the method name.

とあるので、日本語にすると、「メソッド名派生クエリ」とか、「メソッド名生成クエリ」とかになるのかな?

Pleiades翻訳版のほうだと、「メソッド名から派生したクエリ」になっている。

よく忘れるものをメモ

コレクション渡しでの複数条件検索

${フィールド名}In 、否定形なら ${フィールド名}NotIn を使う。毎回 ${フィールド名}s とか書いてエラーにしてるんだよなぁ。

引数として渡すコレクションが空の場合、 IN () に展開されるため、標準SQLに準拠したRDBMSならSQLエラーになる。

数年前、Spring Boot v1.5+Oracle Databaseを使っていた時、OracleのIN1つあたり1,000件上限問題は、自動では回避してくれなかった気がするが、覚えていない。

1,000件ごとに分割して、複数回実行したことがあるような気がする...

LIKE検索

複数パターンあり。

検索条件として %_ワイルドカードが設定された文字列を渡す場合は ${フィールド名}Like 、否定形なら ${フィールド名}NotLike を使う。

ワイルドカードが設定されていない文字列を渡す場合、前方一致、後方一致、部分一致で3パターンの命名方法が用意されている。

  • 前方一致検索: ${フィールド名}StartingWith で引数が 文字列% となる
  • 後方一致検索: ${フィールド名}EndingWith で引数が %文字列 となる
  • 部分一致検索: ${フィールド名}Containing で引数が %文字列% となる

ざっくり、以下のようなイメージ。

String name = "example";
assert repo.findByNameLike(name + "%").equals(repo.findByNameStartingWith(name));
assert repo.findByNameLike("%" + name).equals(repo.findByNameEndingWith(name));
assert repo.findByNameLike("%" + name + "%").equals(repo.findByNameContaining(name));

GROUP BY

直接 GROUP BY に該当するものはない。

findDistinct... で、SELECT DISTINCT による検索ができるので、単純な重複削除はできそう。

DELETE文

検索だけでなく、deleteBy${フィールド名} で削除メソッドも生成できる。Derived Delete QueriesPleiades翻訳版だと派生削除クエリという模様。

delete 以外に remove も利用可能。 AndOr による複数条件も可能。

また、 @Query で検索以外の処理を行う場合、 @Modifying アノテーションをメソッドに付与する必要があるが、メソッド名による精製では @Modifying 不要。

なお、 @Query("DELETE ...") + @Modifying と、メソッド名から生成された削除処理では、実際の挙動が異なる模様。

@Query による削除では、JPQLから生成されたSQLを直接データベースに実行するため、JPAのライフサイクル・コールバック外の削除となり、 @PreRemove@PostRemove などを付与したメソッドがあっても実行されない。

メソッド名派生による削除処理では、実際は同条件での検索処理が実行され、検索結果のJPA Entityを引数として CrudRepository#delete を実行するという処理になるため、JPAのライフサイクル上で削除が行われ、@PreRemove@PostRemove を付与したメソッドも実行されるとのこと。

振り返り

よくよく見ると、Repositoryを継承したインターフェースなら使える模様。

根本原因である「忘れる」ことについては何も解決していないが、まあ、今後はここ見ればいいからいいか...