← Назад к вопросам

Какой метод будем использовать для поиска в Repository?

1.0 Junior🔥 161 комментариев
#Spring Boot и Spring Data

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Методы поиска в Spring Data Repository

Это вопрос о практическом использовании Spring Data JPA для работы с данными. Давайте разберёмся в различных способах поиска в репозитории.

Основные методы Repository

1. Встроенные методы интерфейса CrudRepository

public interface CrudRepository<T, ID> extends Repository<T, ID> {
    // Поиск по ID
    Optional<T> findById(ID id);
    
    // Получить все
    Iterable<T> findAll();
    
    // С пагинацией
    Page<T> findAll(Pageable pageable);
    
    // Сохранение
    <S extends T> S save(S entity);
    
    // Удаление
    void deleteById(ID id);
}

Пример использования:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Унаследованные методы
}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User getUserById(Long id) {
        // findById возвращает Optional
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
    }
    
    public List<User> getAllUsers() {
        return (List<User>) userRepository.findAll();
    }
    
    public Page<User> getUsersPaginated(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return userRepository.findAll(pageable);
    }
}

**2. Query Methods (Производные запросы)

Spring Data генерирует SQL на основе имени метода:

public interface UserRepository extends JpaRepository<User, Long> {
    // Простой поиск по одному полю
    User findByEmail(String email);
    
    // Поиск с Optional
    Optional<User> findByEmail(String email);
    
    // Поиск со списком результатов
    List<User> findByStatus(String status);
    
    // Диапазонный поиск
    List<User> findByAgeBetween(int minAge, int maxAge);
    
    // Поиск по нескольким условиям
    List<User> findByFirstNameAndLastName(String firstName, String lastName);
    
    // Логический оператор OR
    List<User> findByFirstNameOrEmail(String firstName, String email);
    
    // Сравнение
    List<User> findByAgeGreaterThan(int age);
    List<User> findByAgeGreaterThanEqual(int age);
    List<User> findByAgeLessThan(int age);
    
    // LIKE поиск
    List<User> findByEmailContaining(String substring);
    List<User> findByEmailStartingWith(String prefix);
    List<User> findByEmailEndingWith(String suffix);
    
    // Is NULL
    List<User> findByEmailIsNull();
    List<User> findByEmailIsNotNull();
    
    // Сортировка
    List<User> findByStatusOrderByCreatedAtDesc(String status);
    List<User> findByStatus(String status, Sort sort);
    
    // С пагинацией
    Page<User> findByStatus(String status, Pageable pageable);
}

Синтаксис Query Methods:

find + (By | Distinct) + [Entity]By + Criteria + [OrderBy] + [Asc | Desc]

Примеры сложных методов:

public interface UserRepository extends JpaRepository<User, Long> {
    // Поиск с сортировкой
    List<User> findByAgeGreaterThan(int age, Sort sort);
    
    // Использование
    List<User> users = userRepository.findByAgeGreaterThan(25, 
        Sort.by("createdAt").descending()
    );
    
    // С пагинацией
    Page<User> findByStatus(String status, Pageable pageable);
    
    // Использование
    Page<User> users = userRepository.findByStatus("ACTIVE",
        PageRequest.of(0, 20, Sort.by("email").ascending())
    );
}

3. @Query аннотация (JPQL)

Для сложных запросов используй явную JPQL:

public interface UserRepository extends JpaRepository<User, Long> {
    // JPQL (Object-Oriented)
    @Query("SELECT u FROM User u WHERE u.email = :email")
    Optional<User> findByEmailJPQL(@Param("email") String email);
    
    // С условиями
    @Query("SELECT u FROM User u WHERE u.age > :age AND u.status = :status")
    List<User> findActiveUsersOlderThan(
        @Param("age") int age,
        @Param("status") String status
    );
    
    // Со счётом
    @Query("SELECT COUNT(u) FROM User u WHERE u.status = :status")
    int countByStatus(@Param("status") String status);
    
    // С JOIN
    @Query("SELECT u FROM User u JOIN u.orders o WHERE o.totalPrice > :minPrice")
    List<User> findUsersByMinOrderPrice(@Param("minPrice") BigDecimal minPrice);
    
    // С GROUP BY
    @Query("SELECT u.status, COUNT(u) FROM User u GROUP BY u.status")
    List<Object[]> countByStatus();
}

4. @Query с Native SQL

Для database-specific SQL:

public interface UserRepository extends JpaRepository<User, Long> {
    @Query(value = "SELECT * FROM users WHERE email = :email", nativeQuery = true)
    Optional<User> findByEmailSQL(@Param("email") String email);
    
    @Query(value = """
        SELECT u.id, u.email, COUNT(o.id) as order_count
        FROM users u
        LEFT JOIN orders o ON u.id = o.user_id
        WHERE u.status = :status
        GROUP BY u.id, u.email
        HAVING COUNT(o.id) > :minOrders
        """, nativeQuery = true)
    List<Object[]> findUsersWithOrderStats(
        @Param("status") String status,
        @Param("minOrders") int minOrders
    );
}

5. Specification API (для динамических запросов)

Если нужны динамические критерии:

public interface UserRepository extends JpaRepository<User, Long>, 
                                       JpaSpecificationExecutor<User> {
}

public class UserSpecifications {
    public static Specification<User> hasEmail(String email) {
        return (root, query, cb) -> cb.equal(root.get("email"), email);
    }
    
    public static Specification<User> ageGreaterThan(int age) {
        return (root, query, cb) -> cb.greaterThan(root.get("age"), age);
    }
    
    public static Specification<User> hasStatus(String status) {
        return (root, query, cb) -> cb.equal(root.get("status"), status);
    }
}

// Использование
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public List<User> searchUsers(String email, int minAge, String status) {
        Specification<User> spec = Specification.where(null);
        
        if (email != null) {
            spec = spec.and(UserSpecifications.hasEmail(email));
        }
        if (minAge > 0) {
            spec = spec.and(UserSpecifications.ageGreaterThan(minAge));
        }
        if (status != null) {
            spec = spec.and(UserSpecifications.hasStatus(status));
        }
        
        return userRepository.findAll(spec);
    }
}

Сравнение методов

МетодСложностьПроизводительностьГибкостьИспользование
Query MethodsПростаяХорошоСредняяПростые поиски
@Query JPQLСредняяХорошоВысокаяСложные запросы
@Query SQLВысокаяОтличнаяМаксимальнаяDB-specific
SpecificationСредняяХорошоМаксимальнаяДинамические критерии
Example/ExampleMatcherПростаяСреднееСредняяПоиск по примеру

Практический пример: Полнотекстовый поиск

public interface ArticleRepository extends JpaRepository<Article, Long> {
    // Просто
    List<Article> findByTitleContaining(String keyword);
    
    // Сложнее (с рейтингом)
    @Query("""
        SELECT a FROM Article a
        WHERE a.title ILIKE CONCAT('%', :keyword, '%')
           OR a.content ILIKE CONCAT('%', :keyword, '%')
        ORDER BY CASE 
            WHEN a.title ILIKE CONCAT('%', :keyword, '%') THEN 1
            ELSE 2
        END
        """)
    List<Article> searchArticles(@Param("keyword") String keyword);
}

@Service
public class ArticleService {
    @Autowired
    private ArticleRepository articleRepository;
    
    public List<Article> search(String keyword, Pageable pageable) {
        // Для простого случая
        return articleRepository.findByTitleContaining(keyword);
        
        // Для сложного — используй нативный SQL
    }
}

Рекомендации

  • Query Methods: Для простых поисков (до 3 условий)
  • @Query JPQL: Для сложных запросов с JOINs, GROUP BY
  • @Query Native SQL: Когда нужна специфика БД
  • Specification API: Для динамических критериев поиска
  • Всегда используй Optional: Для методов, возвращающих одиночные результаты
  • Используй Page: Для пагинации больших результатов
  • Создавай индексы: На полях, по которым часто ищешь
Какой метод будем использовать для поиска в Repository? | PrepBro