В предыдущем посте я вкратце рассказал о Spring Data JPA и затронул тему «волшебных» методов, приведя простой пример их использования. Однако возможности волшебных методов значительны, благодаря чему ими можно заменить именованные запросы в большинстве случаев.
Создание волшебного метода
Название любого волшебного метода состоит из префикса, условий запроса и параметров сортировки. Обязательным является только префикс. У нас есть следущий метод:
1 2 3 4 |
public interface PersonRepository extends CrudRepository<Person, String> { List<Person> findTop10ByNameOrEmailLikeIgnoreCaseOrderByNameDesc(String name, String email); } |
Он будет преобразован в следующий запрос:
1 |
from Person p where p.name = ?1 or upper(p.email) like upper(?2) order by name desc limit 10 |
Теперь разберём подробнее имя метода.
Префиксы
В нашем пример префикс — findTop10By. Возможные префиксы методов: find…By, get…By, read…By, query…By, count…By, remove…By и delete…By. Вместо … можно добавить ключевое слово Distinct для получение уникальных записей из БД, First… илиTop… (как в нашем случае) для получения определённого количества записей или любой набор слов для упрощеняи понимания логики метода. Исключением является только слово By, так как оно определяет конец префикса.
Название метода может состоять из одного только префикса, если запрос не содержит условий.
Условия
После первого By начинается перечисление условий запроса, которых в нашем случае два: Name и EmailLikeIgnoreCase. В любом условии фигурирует имя свойства класса-сущности, к которому применяется условие, а так же ключевое слово условия, если оно необходимо. Полный список ключевых слов можно найти в официальной документации.
Условия могут применяться к свойствам связанных классов сущностей. Допустим, у нас есть класс Country:
1 2 3 4 5 6 7 8 |
@Entity public class Country { @Id private String id; private String name; //лишний код опущен } |
И у класса Person есть связь с данным классом:
1 2 3 4 5 6 7 |
@Entity public class Person { @ManyToOne private Country country; // лишний код опущен } |
Тогда мы можем написать метод, который бы мог искать записи класса Person по значению свойства name класса Country:
1 2 3 4 |
public interface PersonRepository extends CrudRepository<Person, String> { List<Person> findByCountry_nameLikeIgnoreCase(String country); } |
В этом примере мы обратились при помощи условия Country_nameLikeIgnoreCase к свойству связанного класса-сущности.
Сортировка и ограничение получаемых данных
Сортировку и ограничение количества записей можно реализовать, как уже было сказано, при помощи ключевых слов OrderBy…, First… и Top…. Но это делает методы негибкими и неудобными. При таком подходе придётся для каждого случая писать свой метод, что сильно неудобно. Удобнее будет использование объектов типов Pageable и Sort.
Метод findTop10ByNameOrEmailLikeIgnoreCaseOrderByNameDesc можно переписать следующим образом:
1 2 3 4 |
public interface PersonRepository extends CrudRepository<Person, String> { List<Person> findByNameLikeIgnoreCase(String email, Pageable pageable); } |
Pageable реализует возможности сортировки, поэтому нет необходимости в использовании Sort в данном случае.
При использовании указанных типов мы получаем больше контроля над сортировкой и пейджинацией.
Дополнительные аннотации
Синхронные Query-методы не нуждаются в дополнительных аннотациях. Аннотации нужны только методам, изменяющим записи в БД, а так же асинхронным методам.
1 2 3 4 5 6 7 8 9 |
public interface PersonRepository extends CrudRepository<Person, String> { @Async Future<List<Person>> findByNameLikeIgnoreCase(String email, Pageable pageable); @Modifying @Transactional Integer deleteByEmail(String email); } |
- аннотация @Async указывает, что метод должен выполняться асинхронно.
- @Modifying говорит о том, что указанный метод должен быть интерпретирован как модифицирующий запрос
- @Transactional добавляет поддержку транзакций для указанного метода
Возвращаемые типы
Вы, наверняка, обратили внимание на то, что в предыдущем примере асинхронный метод вернул отбъект типа Future. Методы репозиториев Spring Data могут возвращать не только объекты классов-сущностей и их коллекции, но и объекты многих дргугих типов, которые могут быть применимы в том или ином случае. Полный список возможных возвращаемых типов вы можете найти в официальной документации.