🔧 Spring Data findAll антипаттерн



Spring Data Repository отличная концепция, позволяющая нам абстрагироваться от CRUD операций над domain entity. Достаточно объявить пустой интерфейс и унаследовать его, например, от ListCrudRepository или JpaRepository, если мы работем с JPA.





public interface OwnerRepository extends JpaRepository<Owner, Long> {

}





После этого нам сразу будут доступны основные методы работы с entity - save, update, findById, findAllById, findAll, и т.д. Но некоторые из этих методов могут привести к серъезным проблемам с производительностью и памятью, например метод findAll(). Давайте представим, что нам нужно найти всех Owner у которых есть животные c определенным именем и отсортировать по имени владельца. Поскольку мы находимся всего в одном шаге от вызова findAll() метода для «решения» этой проблемы, не пройдет много времени, пока кто-нибудь не предложит следующее решение:







public List<Owner> findOwnerByPetName(Collection<String> petNames) {

return ownerRepository.findAll()

.stream()

.filter(owner -> owner.getPets()

.stream()

.map(Pet::getName)

.anyMatch(petNames::contains)

)

.sorted(Comparator.comparing(Owner::getFirstName))

.toList();

}





Проблема здесь в том, что мы загрузили всю таблицу Post в память и потом начали фильтровать и сортировать данные, вместо того чтобы сделать это с помощью JPQL или SQL запроса за одно обращение к БД и загрузить в память только те данные, которые нам нужны в дальнейшем. Давайте посмотрим как мог бы выглядеть такой метод репозитория:





//derived method

List<Owner> findByPets_NameInOrderByFirstNameAsc(Collection<String> petNames);



//JPA query method

@Query("select o from Owner o left join o.pets pets where pets.name in ?1 order by o.firstName")

List<Owner> findOwnerByPetName(Collection<String> petNames);







Если у нас в репозитории есть метод findAll(), мы должны понимать что рано или поздно, по мере роста команды, им может кто-то воспользоваться. Возможно следует определять базовый интерфейс репозитория вашем проекте самостоятельно и наследовать его от org.springframework.data.repository.Repository и подконтрольно наполнять его методами.



#SpringData #SpringTips