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

Как разделить нужные и ненужные данные

2.2 Middle🔥 251 комментариев
#Многопоточность

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

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

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

Как разделить нужные и ненужные данные

Это вопрос о фильтрации данных, что является критической задачей при работе с большими объёмами информации. Речь идёт о выборе нужных данных из набора на различных уровнях приложения: от базы данных до пользовательского интерфейса. Эффективное разделение данных существенно влияет на производительность системы.

Уровень 1: На уровне БД (самый эффективный)

Лучший способ — отфильтровать данные там, где они хранятся. Базы данных оптимизированы для фильтрации с использованием индексов.

SQL-подход с WHERE

// ❌ Неправильно: берём ВСЕ данные и фильтруем в Java
List<User> allUsers = repository.findAll();
List<User> activeUsers = allUsers.stream()
    .filter(u -> u.isActive())
    .collect(Collectors.toList());
// Это нагружает память и медленно

// ✅ Правильно: фильтруем на уровне SQL
List<User> activeUsers = repository.findByIsActiveTrue();

Spring Data JPA

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Простая фильтрация
    List<User> findByStatus(UserStatus status);
    
    // Сложные условия
    List<User> findByStatusAndCreatedDateAfter(UserStatus status, LocalDate date);
    
    // Кастомные запросы
    @Query("SELECT u FROM User u WHERE u.status = :status AND u.age > :minAge")
    List<User> findActiveAdults(@Param("status") UserStatus status, 
                                 @Param("minAge") int minAge);
}

Specification для гибкой фильтрации

public class UserSpecifications {
    
    public static Specification<User> isActive() {
        return (root, query, cb) -> cb.isTrue(root.get("isActive"));
    }
    
    public static Specification<User> hasRole(Role role) {
        return (root, query, cb) -> cb.equal(root.get("role"), role);
    }
    
    public static Specification<User> createdAfter(LocalDate date) {
        return (root, query, cb) -> cb.greaterThanOrEqualTo(
            root.get("createdDate"), date);
    }
}

// Использование
Specification<User> spec = 
    UserSpecifications.isActive()
        .and(UserSpecifications.hasRole(Role.ADMIN))
        .and(UserSpecifications.createdAfter(LocalDate.now().minusMonths(1)));

List<User> result = repository.findAll(spec);

Уровень 2: Проекции (SELECT только нужные колонки)

Если нужна не вся сущность, а только несколько полей — используй проекции:

// DTO для минимума данных
public record UserDTO(
    Long id,
    String name,
    String email
) {}

// Repository с проекцией
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    List<UserDTO> findByStatus(UserStatus status);
    
    @Query("SELECT new com.example.UserDTO(u.id, u.name, u.email) " +
           "FROM User u WHERE u.status = :status")
    List<UserDTO> findActiveUsersDTO(@Param("status") UserStatus status);
}

Или через интерфейс-проекцию:

public interface UserProjection {
    Long getId();
    String getName();
    String getEmail();
}

public interface UserRepository extends JpaRepository<User, Long> {
    List<UserProjection> findByStatus(UserStatus status);
}

Уровень 3: Stream API для обработки коллекций

Когда данные уже в памяти, используй Stream для элегантной фильтрации:

public class DataFiltering {
    
    public static void main(String[] args) {
        List<Product> products = getProducts();
        
        // Фильтрация по одному условию
        List<Product> expensive = products.stream()
            .filter(p -> p.getPrice() > 100)
            .collect(Collectors.toList());
        
        // Множественные условия
        List<Product> inStock = products.stream()
            .filter(p -> p.getPrice() > 50)
            .filter(p -> p.isInStock())
            .filter(p -> p.getCategory() == Category.ELECTRONICS)
            .collect(Collectors.toList());
        
        // Или одно условие
        List<Product> filtered = products.stream()
            .filter(p -> p.getPrice() > 50 && 
                        p.isInStock() && 
                        p.getCategory() == Category.ELECTRONICS)
            .collect(Collectors.toList());
        
        // Преобразование и фильтрация
        List<String> names = products.stream()
            .filter(p -> p.getPrice() > 100)
            .map(Product::getName)
            .collect(Collectors.toList());
    }
}

Уровень 4: Пагинация для больших наборов

Не загружай всё сразу — используй пагинацию:

// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByStatus(UserStatus status, Pageable pageable);
}

// Контроллер
@RestController
@RequestMapping("/users")
public class UserController {
    
    @GetMapping
    public Page<UserDTO> listUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(defaultValue = "name") String sort) {
        
        Pageable pageable = PageRequest.of(
            page, 
            size, 
            Sort.by(sort).ascending()
        );
        
        return userRepository.findByStatus(UserStatus.ACTIVE, pageable)
            .map(user -> convertToDTO(user));
    }
}

Уровень 5: MapStruct для трансформации данных

Когда нужна сложная трансформация с фильтрацией:

// DTO с только нужными полями
public record UserDisplayDTO(
    Long id,
    String name,
    String email
) {}

// Mapper
@Mapper(componentModel = "spring")
public interface UserMapper {
    
    UserDisplayDTO toDisplayDTO(User user);
    
    @Mapping(target = "email", source = "user.email")
    UserDisplayDTO toDTO(User user);
}

// Использование
@Service
public class UserService {
    
    @Autowired
    private UserMapper mapper;
    
    public List<UserDisplayDTO> getActiveUsers() {
        return userRepository.findByStatus(UserStatus.ACTIVE)
            .stream()
            .map(mapper::toDisplayDTO)
            .collect(Collectors.toList());
    }
}

Уровень 6: Caching для избежания повторной обработки

Частые фильтрации — кэшируй результаты:

@Service
public class UserService {
    
    @Cacheable(value = "activeUsers")
    public List<User> getActiveUsers() {
        return userRepository.findByStatus(UserStatus.ACTIVE);
    }
    
    @CacheEvict(value = "activeUsers", allEntries = true)
    public User updateUser(User user) {
        return userRepository.save(user);
    }
}

Пример комплексного решения

@Service
public class ProductSearchService {
    
    @Autowired
    private ProductRepository repository;
    
    @Autowired
    private ProductMapper mapper;
    
    // 1. Фильтрация на БД уровне
    // 2. Проекция только нужных полей
    // 3. Пагинация
    public Page<ProductDTO> searchProducts(
            String category,
            BigDecimal minPrice,
            BigDecimal maxPrice,
            Pageable pageable) {
        
        Specification<Product> spec = Specification
            .where(ProductSpecifications.inCategory(category))
            .and(ProductSpecifications.priceBetween(minPrice, maxPrice))
            .and(ProductSpecifications.inStock());
        
        return repository.findAll(spec, pageable)
            .map(mapper::toDTO);
    }
}

Чеклист оптимизации

Фильтруй на уровне БД — используй WHERE в SQL
Используй проекции — SELECT только нужные колонки
Примени пагинацию — не загружай всё в память
Кэшируй результаты — избегай повторных вычислений
Используй индексы — добавь индексы на часто фильтруемые поля
Профилируй запросы — следи за количеством запросов (N+1 проблема)

Самая частая ошибка: N+1 SELECT проблема

// ❌ N+1: один запрос за пользователя + один общий
List<User> users = repository.findAll();
for (User user : users) {
    System.out.println(user.getOrders().size()); // N дополнительных запросов!
}

// ✅ Правильно: JOIN FETCH
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();

Правильное разделение данных — это баланс между производительностью (меньше данных в памяти) и функциональностью (иметь то, что нужно).