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

Какие знаешь способы делать запросы к базе данных через Spring Data?

2.2 Middle🔥 171 комментариев
#Spring Boot и Spring Data#Spring Framework

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

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

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

Способы делать запросы к БД через Spring Data

Spring Data предоставляет мощный и гибкий API для работы с БД. Расскажу о различных способах, от самых простых до продвинутых.

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

Наиболее простой и удобный способ. Spring автоматически генерирует реализацию по имени метода.

public interface UserRepository extends JpaRepository<User, UUID> {
    // Простой поиск
    Optional<User> findByUsername(String username);
    Optional<User> findByEmail(String email);
    
    // Поиск с условиями
    List<User> findByAgeGreaterThan(int age);
    List<User> findByAgeBetween(int minAge, int maxAge);
    List<User> findByStatusEquals(UserStatus status);
    
    // Contains / StartingWith / EndingWith (для строк)
    List<User> findByUsernameContaining(String username);
    List<User> findByUsernameStartingWith(String prefix);
    List<User> findByEmailEndingWith(String domain);
    
    // Case insensitive
    Optional<User> findByUsernameIgnoreCase(String username);
    
    // NOT условия
    List<User> findByStatusNot(UserStatus status);
    List<User> findByEmailIsNull();
    List<User> findByEmailIsNotNull();
    
    // Boolean проверки
    List<User> findByActiveTrue();
    List<User> findByActiveFalse();
    
    // Сортировка
    List<User> findByActiveOrderByCreatedAtDesc(boolean active);
    List<User> findByStatusOrderByUsernameAsc(UserStatus status);
    
    // Лимит результатов
    List<User> findFirst10ByOrderByCreatedAtDesc();
    Optional<User> findFirstByOrderByUpdatedAtDesc();
}

2. @Query Annotation (JPQL)

Для более сложных запросов используем JPQL с явно написанными SQL запросами.

public interface UserRepository extends JpaRepository<User, UUID> {
    // Базовый запрос
    @Query("SELECT u FROM User u WHERE u.username = :username")
    Optional<User> findByUsernameJpql(@Param("username") String username);
    
    // С JOIN
    @Query("SELECT u FROM User u " +
            "JOIN FETCH u.profile p " +
            "WHERE u.status = :status")
    List<User> findActiveUsersWithProfiles(
        @Param("status") UserStatus status
    );
    
    // С GROUP BY и агрегацией
    @Query("SELECT new map(u.country as country, COUNT(u) as count) " +
            "FROM User u GROUP BY u.country")
    List<Map<String, Object>> countUsersByCountry();
    
    // С CASE WHEN
    @Query("SELECT new map(u.id as id, u.username as username, " +
            "CASE WHEN u.age < 18 THEN 'minor' ELSE 'adult' END as ageGroup) " +
            "FROM User u")
    List<Map<String, Object>> findUsersWithAgeGroup();
    
    // Subquery
    @Query("SELECT u FROM User u WHERE u.id IN " +
            "(SELECT p.user.id FROM Purchase p WHERE p.totalPrice > :amount)")
    List<User> findUsersByMinimumPurchaseAmount(
        @Param("amount") BigDecimal amount
    );
}

3. Native Queries (SQL)

Для специфичных для БД оптимизаций или когда JPQL недостаточно.

public interface UserRepository extends JpaRepository<User, UUID> {
    // Простой native query
    @Query(
        value = "SELECT * FROM users WHERE created_at > :date",
        nativeQuery = true
    )
    List<User> findRecentUsers(@Param("date") LocalDateTime date);
    
    // С projection
    @Query(
        value = "SELECT id, username, email FROM users WHERE active = true",
        nativeQuery = true
    )
    List<UserProjection> findActiveUsersProjection();
    
    // Сложный запрос с оконными функциями
    @Query(
        value = "SELECT id, username, " +
                "ROW_NUMBER() OVER (ORDER BY created_at DESC) as row_num " +
                "FROM users",
        nativeQuery = true
    )
    List<UserWithRowNumber> findAllWithRowNumbers();
}

4. Specifications (Dynamic Queries)

Для построения динамичных запросов во время выполнения.

// Спецификация для User
public class UserSpecifications {
    public static Specification<User> hasUsername(String username) {
        return (root, query, criteriaBuilder) ->
            criteriaBuilder.like(
                criteriaBuilder.lower(root.get("username")),
                "%" + username.toLowerCase() + "%"
            );
    }
    
    public static Specification<User> hasEmail(String email) {
        return (root, query, criteriaBuilder) ->
            criteriaBuilder.equal(root.get("email"), email);
    }
    
    public static Specification<User> hasStatus(UserStatus status) {
        return (root, query, criteriaBuilder) ->
            criteriaBuilder.equal(root.get("status"), status);
    }
    
    public static Specification<User> createdAfter(LocalDateTime date) {
        return (root, query, criteriaBuilder) ->
            criteriaBuilder.greaterThan(root.get("createdAt"), date);
    }
}

// Repository с поддержкой Specification
public interface UserRepository extends JpaRepository<User, UUID>,
        JpaSpecificationExecutor<User> {
}

// Использование
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public List<User> searchUsers(UserSearchCriteria criteria) {
        Specification<User> spec = Specification.where(null);
        
        if (criteria.getUsername() != null) {
            spec = spec.and(UserSpecifications.hasUsername(criteria.getUsername()));
        }
        
        if (criteria.getStatus() != null) {
            spec = spec.and(UserSpecifications.hasStatus(criteria.getStatus()));
        }
        
        if (criteria.getCreatedAfter() != null) {
            spec = spec.and(UserSpecifications.createdAfter(criteria.getCreatedAfter()));
        }
        
        return userRepository.findAll(spec);
    }
}

5. Criteria API (Type-safe Queries)

Полностью type-safe способ строить запросы программно.

@Service
public class UserSearchService {
    @PersistenceContext
    private EntityManager entityManager;
    
    public List<User> findUsersWithCriteria(String username, UserStatus status) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> root = query.from(User.class);
        
        List<Predicate> predicates = new ArrayList<>();
        
        if (username != null && !username.isEmpty()) {
            predicates.add(
                cb.like(cb.lower(root.get("username")),
                        "%" + username.toLowerCase() + "%")
            );
        }
        
        if (status != null) {
            predicates.add(
                cb.equal(root.get("status"), status)
            );
        }
        
        if (!predicates.isEmpty()) {
            query.where(cb.and(predicates.toArray(new Predicate[0])));
        }
        
        return entityManager.createQuery(query).getResultList();
    }
}

6. QueryDSL

Мощная библиотека для type-safe запросов с удобным API.

// Нужна зависимость
// <dependency>
//     <groupId>com.querydsl</groupId>
//     <artifactId>querydsl-jpa</artifactId>
// </dependency>

@Repository
public interface UserRepository extends JpaRepository<User, UUID>,
        QuerydslPredicateExecutor<User> {
}

// Использование
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public List<User> searchWithQueryDSL(String username, int minAge) {
        QUser user = QUser.user;
        
        BooleanBuilder predicate = new BooleanBuilder();
        
        if (username != null) {
            predicate.and(user.username.containsIgnoreCase(username));
        }
        
        predicate.and(user.age.goe(minAge));
        predicate.and(user.active.isTrue());
        
        return userRepository.findAll(predicate);
    }
}

7. Custom Repository Methods

Для совсем сложных случаев, когда нужен полный контроль.

public interface UserRepositoryCustom {
    List<User> findWithComplexLogic(String criteria);
}

public class UserRepositoryImpl implements UserRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public List<User> findWithComplexLogic(String criteria) {
        String jpql = "SELECT u FROM User u " +
                     "WHERE u.username LIKE :criteria " +
                     "OR u.email LIKE :criteria " +
                     "ORDER BY u.createdAt DESC";
        
        return entityManager.createQuery(jpql, User.class)
            .setParameter("criteria", "%" + criteria + "%")
            .setMaxResults(100)
            .getResultList();
    }
}

public interface UserRepository extends JpaRepository<User, UUID>,
        UserRepositoryCustom {
}

8. Pagination и Sorting

public interface UserRepository extends JpaRepository<User, UUID> {
    Page<User> findByStatus(UserStatus status, Pageable pageable);
    
    Slice<User> findByActiveTrue(Pageable pageable);
}

// Использование
@GetMapping("/users")
public Page<UserDTO> getUsers(
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "20") int size,
    @RequestParam(defaultValue = "createdAt,desc") String[] sort
) {
    Sort sortOrder = Sort.by(Sort.Direction.DESC, "createdAt");
    Pageable pageable = PageRequest.of(page, size, sortOrder);
    
    return userRepository.findByStatus(ACTIVE, pageable)
        .map(UserDTO::fromEntity);
}

9. Batch Operations

public interface UserRepository extends JpaRepository<User, UUID> {
    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.status = :newStatus WHERE u.status = :oldStatus")
    int updateStatus(
        @Param("oldStatus") UserStatus oldStatus,
        @Param("newStatus") UserStatus newStatus
    );
    
    @Modifying
    @Transactional
    @Query("DELETE FROM User u WHERE u.createdAt < :date")
    int deleteOlderThan(@Param("date") LocalDateTime date);
}

10. Projections

Если нужны только определённые поля.

// DTO Projection
public interface UserSummary {
    UUID getId();
    String getUsername();
    String getEmail();
}

public interface UserRepository extends JpaRepository<User, UUID> {
    List<UserSummary> findByStatus(UserStatus status);
    
    @Query("SELECT new com.example.dto.UserDTO(u.id, u.username, u.email) " +
            "FROM User u WHERE u.active = true")
    List<UserDTO> findActiveUsersDTO();
}

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

МетодПростотаПроизводительностьГибкость
Query MethodsОчень высокаяХорошаяСредняя
@Query (JPQL)ВысокаяХорошаяВысокая
Native SQLСредняяОтличнаяОчень высокая
SpecificationsСредняяХорошаяОчень высокая
Criteria APIНизкаяХорошаяОчень высокая
QueryDSLСредняяХорошаяВысокая

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

  1. Начинай с Query Methods — они покрывают 80% случаев
  2. Переходи на @Query когда методов недостаточно
  3. Используй Native SQL только для БД-специфичных оптимизаций
  4. Specifications для динамических запросов из UI фильтров
  5. QueryDSL когда нужна мощь и читаемость

Заключение

Spring Data предоставляет инструменты для любого сценария. Выбор метода зависит от сложности запроса и требований к гибкости.

Какие знаешь способы делать запросы к базе данных через Spring Data? | PrepBro