Java for Beginner
675 subscribers
559 photos
156 videos
12 files
857 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Пагинация и фильтрация данных в REST API с использованием Spring

При работе с большими наборами данных в REST API критически важна поддержка пагинации и фильтрации. Они позволяют улучшить производительность приложения, сократить нагрузку на сервер и предоставить клиентам удобные способы получать нужные данные.

1. Пагинация (Pagination) в Spring

Пагинация позволяет разделить большие объемы данных на страницы и возвращать их по частям. В Spring Boot пагинация реализуется с помощью Spring Data JPA.

Подключение пагинации в проекте

Spring Data JPA предоставляет интерфейсы и аннотации для легкой реализации пагинации.

Добавьте параметр Pageable в методы репозитория:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {

Page<User> findAll(Pageable pageable);

}


Page — это контейнер, который содержит данные текущей страницы и мета-информацию (например, общее количество страниц).
Pageable — это интерфейс для параметров пагинации: номер страницы, размер страницы и сортировка.


Использование Pageable в контроллере:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserRepository userRepository;

@Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}

@GetMapping
public ResponseEntity<Page<User>> getAllUsers(Pageable pageable) {
Page<User> users = userRepository.findAll(pageable);
return ResponseEntity.ok(users);
}
}


Пример запроса с пагинацией:
GET /api/users?page=0&size=5&sort=name,asc
page — номер страницы (начинается с 0).
size — количество элементов на странице.
sort — параметры сортировки (поле,направление).


Пример ответа:
{
"content": [
{"id": 1, "name": "John", "email": "john@example.com"},
{"id": 2, "name": "Jane", "email": "jane@example.com"}
],
"pageable": {
"pageNumber": 0,
"pageSize": 5
},
"totalPages": 2,
"totalElements": 10,
"last": false
}


2. Фильтрация данных в REST API

Фильтрация позволяет клиентам задавать условия, по которым нужно отбирать данные.


Подходы к фильтрации

Фильтрация по параметрам запроса (Request Parameters): Используется для простой фильтрации данных.

Динамическая фильтрация с помощью Specification или Criteria:
Применяется для сложных и многоуровневых условий.

Фильтрация по параметрам запроса
Реализация простого поиска с фильтрацией:

Добавьте методы в репозиторий:
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {

List<User> findByNameContaining(String name);

List<User> findByAgeGreaterThanEqual(Integer age);

}


#Java #Training #Spring #Pagination #Filter
Добавьте фильтры в контроллер:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserRepository userRepository;

@Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}

@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer age) {

if (name != null) {
return ResponseEntity.ok(userRepository.findByNameContaining(name));
} else if (age != null) {
return ResponseEntity.ok(userRepository.findByAgeGreaterThanEqual(age));
} else {
return ResponseEntity.ok(userRepository.findAll());
}
}
}


Примеры запросов:

Фильтрация по имени:
GET /api/users/search?name=John


Фильтрация по возрасту:
GET /api/users/search?age=30


Динамическая фильтрация с использованием Specification
Для сложных фильтров можно использовать Spring Data JPA Specification.

Добавьте зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>


Создайте Specification для фильтрации:
import org.springframework.data.jpa.domain.Specification;

public class UserSpecification {

public static Specification<User> hasName(String name) {
return (root, query, cb) -> cb.like(root.get("name"), "%" + name + "%");
}

public static Specification<User> hasAgeGreaterThan(Integer age) {
return (root, query, cb) -> cb.greaterThanOrEqualTo(root.get("age"), age);
}
}


Расширьте репозиторий:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

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


Добавьте динамическую фильтрацию в контроллер:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserRepository userRepository;

@Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}

@GetMapping("/filter")
public ResponseEntity<List<User>> filterUsers(
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer age) {

Specification<User> spec = Specification.where(null);

if (name != null) {
spec = spec.and(UserSpecification.hasName(name));
}

if (age != null) {
spec = spec.and(UserSpecification.hasAgeGreaterThan(age));
}

return ResponseEntity.ok(userRepository.findAll(spec));
}
}


Пример запроса с фильтрацией:

GET /api/users/filter?name=Jane&age=25


3. Пагинация и фильтрация вместе

Совмещение пагинации и фильтрации:
@GetMapping("/filter")
public ResponseEntity<Page<User>> filterUsers(
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer age,
Pageable pageable) {

Specification<User> spec = Specification.where(null);

if (name != null) {
spec = spec.and(UserSpecification.hasName(name));
}

if (age != null) {
spec = spec.and(UserSpecification.hasAgeGreaterThan(age));
}

Page<User> result = userRepository.findAll(spec, pageable);
return ResponseEntity.ok(result);
}


Пример запроса:
GET /api/users/filter?name=Jane&page=0&size=5&sort=age,desc


#Java #Training #Spring #Pagination #Filter
@Filter в Hibernate

Аннотация @Filter позволяет динамически применять условия фильтрации к сущностям или коллекциям на уровне базы данных. Она используется для ограничения выборки данных в зависимости от заданных параметров. Находится в пакете org.hibernate.annotations.

Параметры и настройки

Аннотация @Filter имеет следующие атрибуты:

name (обязательный, String):
Уникальное имя фильтра, которое используется для его активации/деактивации в сессии.
Пример:
@Filter(name = "activeUserFilter").

condition (необязательный, String):
SQL-условие, которое добавляется в запрос при активации фильтра.
Может содержать параметры (например, :param), которые задаются во время выполнения.
Пример:
@Filter(name = "activeUserFilter", condition = "is_active = :activeStatus").

deduceAliasInjectionPoints (необязательный, boolean, default = true):
Определяет, должен ли Hibernate автоматически подставлять алиасы таблиц в условие.
Если false, условие должно содержать явные алиасы.


Жизненный цикл фильтра

Объявление фильтра:
Фильтр определяется на уровне класса сущности (@Entity) или коллекции (@ElementCollection, @OneToMany и др.).

Пример:
@Entity
@FilterDef(name = "activeUserFilter",
condition = "is_active = :activeStatus",
parameters = @ParamDef(name = "activeStatus", type = boolean.class))
@Filter(name = "activeUserFilter")
public class User { ... }


Активация фильтра в сессии:
Фильтр не активен по умолчанию, его нужно включать явно в Session или EntityManager.

Пример:
session.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);


Применение в запросах:
После активации фильтр автоматически добавляет условие ко всем запросам, связанным с сущностью.
Для
@OneToMany и других коллекций фильтр применяется при загрузке ассоциаций.

Деактивация фильтра:
Фильтр можно отключить:
session.disableFilter("activeUserFilter");


Механизмы Hibernate и Spring, связанные с @Filter

1. @FilterDef и @ParamDef
@FilterDef определяет фильтр и его параметры.
@ParamDef задает тип параметра (например, String, boolean).
Должен быть указан на уровне класса (обычно на
@Entity или в package-info.java).

2. Интеграция с Spring Data JPA
Фильтры можно активировать в Spring-репозиториях через EntityManager:
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;

public List<User> findActiveUsers() {
em.unwrap(Session.class)
.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
return em.createQuery("FROM User", User.class).getResultList();
}
}


3. Глобальная настройка фильтров в Spring Boot
Можно автоматически включать фильтры для всех запросов через @PostConstruct:
@Component
public class HibernateFilterConfig {
@PersistenceContext
private EntityManager em;

@PostConstruct
public void init() {
Session session = em.unwrap(Session.class);
session.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
}
}


Варианты настройки и применения

1. Фильтрация коллекций
@Entity
public class Department {
@OneToMany(mappedBy = "department")
@Filter(name = "activeUserFilter", condition = "is_active = :activeStatus")
private List<User> users;
}


2. Множественные фильтры
@Entity
@FilterDef(name = "activeFilter", condition = "is_active = :active")
@FilterDef(name = "roleFilter", condition = "role = :roleName")
@Filter(name = "activeFilter")
@Filter(name = "roleFilter")
public class User { ... }


3. Динамическое управление фильтрами
// Включение разных параметров в runtime
session.enableFilter("activeUserFilter")
.setParameter("activeStatus", true);
session.enableFilter("roleFilter")
.setParameter("roleName", "ADMIN");


#Java #Training #Hard #Spring #Hibernate #Filter #FilterDef