Oleg Atamanenko
Рассмотрим подробнее одну из наиболее полезных вещей в Spring Data JPA - генерация JPQL-запросов на основе имени метода.
Spring Data JPA умеет автоматически генерировать запросы используя для подсказки название метода.
Например, метод User.findByLoginAndPassword
сгенерирует примерно следующий код:
FROM User u where u.login = :login and password = :password
Вообще Spring Data JPA пытается быть умным, поэтому реализация findBy{…} методов ищется следующим образом:
У @Query следующие плюсы:
Очевидно, что при использовании запросов нам необходимо каким-то образом указывать параметры для запросов. Для этого есть аннотация @Param:
@Query("select u from User u where u.login = :login and u.password = :password")
Page<User> findByLoginAndPassword(@Param("login") String login, @Param("password") String password);
Кроме того, Spring Data JPA поддерживает отличную концепцию спецификаций.
Спецификации позволяют делать составлять сложные запросы из набора простых.
Для поддержки спецификаций необходимо объявить метод в репозитории:
Page<User> findAll(Specification<User> spec, Pageable pageable);
Спецификация по сути является фильтром и позволяет комбинирование фильтров, что даёт мощный инструмент для построения запросов.
Пример использования спецификаций:
Объявляем наши спецификации:
public static Specification<User> firstNameOrLastNameOrLoginLike(final String search) {
return new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Predicate loginPredicate = builder.like(root.get(User_.login), search);
Predicate firstnamePredicate = builder.like(root.get(User_.firstname), search);
Predicate lastnamePredicate = builder.like(root.get(User_.lastname), search);
return builder.or(loginPredicate, firstnamePredicate, lastnamePredicate);
}
};
}
public static Specification<User> hasRole(final Role role) {
return new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return builder.equal(root.get(User_.role), role);
}
};
}
И в нашем сервисе комбинируем их:
public Page<User> searchUser(Role role, String search, Pageable pageable) {
Specifications<User> mainSpec = where(hasRole(role));
// уточняем запрос, если была передана строка для поиска
if (StringUtils.isNotBlank(search)) {
mainSpec = mainSpec.and(firstNameOrLastNameOrLoginLike(search));
}
return userRepository.findAll(mainSpec, pageable);
}