Spring Boot Topic
Spring Boot Pagination
1. Introduction to Pagination
Pagination is a technique used to divide large datasets into smaller, manageable chunks (pages). It improves performance, reduces bandwidth usage, and enhances user experience.
Benefits of Pagination:
-
Performance: Faster response times with smaller datasets
-
Scalability: Handles large datasets efficiently
-
User Experience: Better navigation and loading times
-
Resource Management: Reduced memory and bandwidth usage
2. Spring Data JPA Pagination
2.1 Repository with Pagination Support
Spring Data JPA provides built-in support for pagination through the PagingAndSortingRepository interface.
Repository Interface:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Basic pagination method
Page<User> findAll(Pageable pageable);
// Pagination with filtering
Page<User> findByActiveTrue(Pageable pageable);
Page<User> findByEmailContaining(String email, Pageable pageable);
Page<User> findByUsernameContainingIgnoreCase(String username, Pageable pageable);
Page<User> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end, Pageable pageable);
// Pagination with joins
@Query("SELECT u FROM User u JOIN u.roles r WHERE r.name = :roleName")
Page<User> findByRoleName(@Param("roleName") String roleName, Pageable pageable);
// Custom pagination with complex query
@Query("SELECT u FROM User u WHERE " +
"(:username IS NULL OR u.username LIKE %:username%) AND " +
"(:email IS NULL OR u.email LIKE %:email%) AND " +
"(:active IS NULL OR u.active = :active)")
Page<User> findUsersByCriteria(
@Param("username") String username,
@Param("email") String email,
@Param("active") Boolean active,
Pageable pageable
);
// Count query for performance optimization
@Query(value = "SELECT u FROM User u WHERE u.active = true",
countQuery = "SELECT COUNT(u) FROM User u WHERE u.active = true")
Page<User> findActiveUsersWithCount(Pageable pageable);
// Native query with pagination
@Query(value = "SELECT * FROM users u WHERE u.created_at >= :startDate",
countQuery = "SELECT COUNT(*) FROM users u WHERE u.created_at >= :startDate",
nativeQuery = true)
Page<User> findUsersCreatedAfter(@Param("startDate") LocalDateTime startDate, Pageable pageable);
}
2.2 Service Layer Implementation
Service Class:
@Service
@Transactional(readOnly = true)
@Slf4j
public class UserService {
@Autowired
private UserRepository userRepository;
// Basic pagination
public Page<User> getAllUsers(Pageable pageable) {
log.info("Fetching users with pagination: page {}, size {}",
pageable.getPageNumber(), pageable.getPageSize());
return userRepository.findAll(pageable);
}
// Pagination with filtering
public Page<User> getActiveUsers(Pageable pageable) {
return userRepository.findByActiveTrue(pageable);
}
public Page<User> searchUsersByEmail(String email, Pageable pageable) {
return userRepository.findByEmailContaining(email, pageable);
}
public Page<User> searchUsersByUsername(String username, Pageable pageable) {
return userRepository.findByUsernameContainingIgnoreCase(username, pageable);
}
// Advanced search with multiple criteria
public Page<User> searchUsers(UserSearchCriteria criteria, Pageable pageable) {
return userRepository.findUsersByCriteria(
criteria.getUsername(),
criteria.getEmail(),
criteria.getActive(),
pageable
);
}
// Pagination with date range
public Page<User> getUsersCreatedBetween(LocalDateTime start, LocalDateTime end, Pageable pageable) {
return userRepository.findByCreatedAtBetween(start, end, pageable);
}
// Get users by role with pagination
public Page<User> getUsersByRole(String roleName, Pageable pageable) {
return userRepository.findByRoleName(roleName, pageable);
}
// Custom pagination with sorting
public Page<User> getUsersWithCustomSorting(Pageable pageable) {
// You can modify pageable or create custom sorting logic here
return userRepository.findAll(pageable);
}
// Get page information without content (for metadata)
public Page<User> getUsersMetadata(Pageable pageable) {
Page<User> page = userRepository.findAll(pageable);
// Log pagination information
logPaginationInfo(page);
return page;
}
private void logPaginationInfo(Page<User> page) {
log.info("Page Number: {}", page.getNumber());
log.info("Page Size: {}", page.getSize());
log.info("Total Elements: {}", page.getTotalElements());
log.info("Total Pages: {}", page.getTotalPages());
log.info("Has Next: {}", page.hasNext());
log.info("Has Previous: {}", page.hasPrevious());
log.info("Is First: {}", page.isFirst());
log.info("Is Last: {}", page.isLast());
}
}
2.3 Custom Pagination Utility Class
Pagination Utility:
@Component
public class PaginationUtils {
public static <T> Page<T> createPage(List<T> content, Pageable pageable, long total) {
return new PageImpl<>(content, pageable, total);
}
public static Pageable createPageable(int page, int size, String sortBy, String direction) {
Sort sort = direction.equalsIgnoreCase("desc")
? Sort.by(sortBy).descending()
: Sort.by(sortBy).ascending();
return PageRequest.of(page, size, sort);
}
public static Pageable createPageable(int page, int size, Sort sort) {
return PageRequest.of(page, size, sort);
}
public static Pageable createPageable(int page, int size) {
return PageRequest.of(page, size);
}
public static <T> PagedResponse<T> createPagedResponse(Page<T> page) {
PagedResponse<T> response = new PagedResponse<>();
response.setContent(page.getContent());
response.setPageNumber(page.getNumber());
response.setPageSize(page.getSize());
response.setTotalElements(page.getTotalElements());
response.setTotalPages(page.getTotalPages());
response.setLast(page.isLast());
response.setFirst(page.isFirst());
response.setEmpty(page.isEmpty());
response.setNumberOfElements(page.getNumberOfElements());
// Add pagination links
if (page.hasPrevious()) {
response.setPreviousPage(page.getNumber() - 1);
}
if (page.hasNext()) {
response.setNextPage(page.getNumber() + 1);
}
return response;
}
public static void validatePageable(Pageable pageable) {
if (pageable.getPageNumber() < 0) {
throw new IllegalArgumentException("Page number cannot be less than zero");
}
if (pageable.getPageSize() < 1) {
throw new IllegalArgumentException("Page size must be at least 1");
}
if (pageable.getPageSize() > 100) {
throw new IllegalArgumentException("Page size cannot be greater than 100");
}
}
}
3. Controller Implementation
3.1 REST Controller with Pagination
User Controller:
@RestController
@RequestMapping("/api/users")
@Validated
@Slf4j
public class UserController {
@Autowired
private UserService userService;
// Get all users with pagination
@GetMapping
public ResponseEntity<PagedResponse<User>> getAllUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String direction) {
Pageable pageable = PaginationUtils.createPageable(page, size, sortBy, direction);
PaginationUtils.validatePageable(pageable);
Page<User> userPage = userService.getAllUsers(pageable);
PagedResponse<User> response = PaginationUtils.createPagedResponse(userPage);
return ResponseEntity.ok(response);
}
// Search users with pagination
@GetMapping("/search")
public ResponseEntity<PagedResponse<User>> searchUsers(
@RequestParam(required = false) String username,
@RequestParam(required = false) String email,
@RequestParam(required = false) Boolean active,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "username") String sortBy,
@RequestParam(defaultValue = "asc") String direction) {
Pageable pageable = PaginationUtils.createPageable(page, size, sortBy, direction);
UserSearchCriteria criteria = new UserSearchCriteria(username, email, active);
Page<User> userPage = userService.searchUsers(criteria, pageable);
PagedResponse<User> response = PaginationUtils.createPagedResponse(userPage);
return ResponseEntity.ok(response);
}
// Get active users with pagination
@GetMapping("/active")
public ResponseEntity<PagedResponse<User>> getActiveUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<User> userPage = userService.getActiveUsers(pageable);
PagedResponse<User> response = PaginationUtils.createPagedResponse(userPage);
return ResponseEntity.ok(response);
}
// Get users by email containing
@GetMapping("/search/email")
public ResponseEntity<PagedResponse<User>> searchUsersByEmail(
@RequestParam String email,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<User> userPage = userService.searchUsersByEmail(email, pageable);
PagedResponse<User> response = PaginationUtils.createPagedResponse(userPage);
return ResponseEntity.ok(response);
}
// Get users by role with pagination
@GetMapping("/role/{roleName}")
public ResponseEntity<PagedResponse<User>> getUsersByRole(
@PathVariable String roleName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<User> userPage = userService.getUsersByRole(roleName, pageable);
PagedResponse<User> response = PaginationUtils.createPagedResponse(userPage);
return ResponseEntity.ok(response);
}
// Advanced search with multiple sorting
@GetMapping("/advanced-search")
public ResponseEntity<PagedResponse<User>> advancedSearch(
@RequestParam(required = false) String username,
@RequestParam(required = false) String email,
@RequestParam(required = false) Boolean active,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "username,asc") String[] sort) {
// Parse multiple sort parameters
Sort sorting = parseSortParameters(sort);
Pageable pageable = PageRequest.of(page, size, sorting);
UserSearchCriteria criteria = new UserSearchCriteria(username, email, active);
Page<User> userPage = userService.searchUsers(criteria, pageable);
PagedResponse<User> response = PaginationUtils.createPagedResponse(userPage);
return ResponseEntity.ok(response);
}
// Custom method to parse multiple sort parameters
private Sort parseSortParameters(String[] sort) {
if (sort == null || sort.length == 0) {
return Sort.by("id").ascending();
}
List<Sort.Order> orders = new ArrayList<>();
for (String sortParam : sort) {
String[] sortParts = sortParam.split(",");
if (sortParts.length == 2) {
String property = sortParts[0];
String direction = sortParts[1];
Sort.Order order = direction.equalsIgnoreCase("desc")
? Sort.Order.desc(property)
: Sort.Order.asc(property);
orders.add(order);
}
}
return Sort.by(orders);
}
}
3.2 Response DTO Classes
PagedResponse DTO:
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PagedResponse<T> {
private List<T> content;
private int pageNumber;
private int pageSize;
private long totalElements;
private int totalPages;
private boolean last;
private boolean first;
private boolean empty;
private int numberOfElements;
// Navigation properties
private Integer previousPage;
private Integer nextPage;
// Metadata
private Map<String, Object> metadata;
public PagedResponse(List<T> content, int pageNumber, int pageSize,
long totalElements, int totalPages) {
this.content = content;
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.totalElements = totalElements;
this.totalPages = totalPages;
this.last = (pageNumber + 1) >= totalPages;
this.first = pageNumber == 0;
this.empty = content == null || content.isEmpty();
this.numberOfElements = content != null ? content.size() : 0;
// Set navigation
if (!first) {
this.previousPage = pageNumber - 1;
}
if (!last) {
this.nextPage = pageNumber + 1;
}
}
public void addMetadata(String key, Object value) {
if (this.metadata == null) {
this.metadata = new HashMap<>();
}
this.metadata.put(key, value);
}
}
Search Criteria DTO:
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserSearchCriteria {
private String username;
private String email;
private Boolean active;
private LocalDateTime createdAfter;
private LocalDateTime createdBefore;
public UserSearchCriteria(String username, String email, Boolean active) {
this.username = username;
this.email = email;
this.active = active;
}
public boolean hasFilters() {
return username != null || email != null || active != null ||
createdAfter != null || createdBefore != null;
}
}
4. Advanced Pagination Features
4.1 Custom Pagination with Specifications
Specification-based Pagination:
@Service
@Transactional(readOnly = true)
public class UserSpecificationService {
@Autowired
private UserRepository userRepository;
public Page<User> findUsersWithSpecification(UserSearchCriteria criteria, Pageable pageable) {
Specification<User> spec = buildSpecification(criteria);
return userRepository.findAll(spec, pageable);
}
private Specification<User> buildSpecification(UserSearchCriteria criteria) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.hasText(criteria.getUsername())) {
predicates.add(criteriaBuilder.like(
criteriaBuilder.lower(root.get("username")),
"%" + criteria.getUsername().toLowerCase() + "%"
));
}
if (StringUtils.hasText(criteria.getEmail())) {
predicates.add(criteriaBuilder.like(
criteriaBuilder.lower(root.get("email")),
"%" + criteria.getEmail().toLowerCase() + "%"
));
}
if (criteria.getActive() != null) {
predicates.add(criteriaBuilder.equal(root.get("active"), criteria.getActive()));
}
if (criteria.getCreatedAfter() != null) {
predicates.add(criteriaBuilder.greaterThanOrEqualTo(
root.get("createdAt"), criteria.getCreatedAfter()
));
}
if (criteria.getCreatedBefore() != null) {
predicates.add(criteriaBuilder.lessThanOrEqualTo(
root.get("createdAt"), criteria.getCreatedBefore()
));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
4.2 Pagination with Projections
Interface-based Projections:
public interface UserProjection {
Long getId();
String getUsername();
String getEmail();
boolean isActive();
@Value("#{target.username + ' (' + target.email + ')'}")
String getDisplayName();
}
// Repository methods with projections
public interface UserRepository extends JpaRepository<User, Long> {
<T> Page<T> findByActiveTrue(Pageable pageable, Class<T> type);
@Query("SELECT u.id as id, u.username as username, u.email as email, u.active as active FROM User u")
Page<UserProjection> findAllProjectedBy(Pageable pageable);
Page<UserSummary> findByEmailContaining(String email, Pageable pageable);
}
// Custom projection interface
public interface UserSummary {
String getUsername();
String getEmail();
Long getPostCount();
default String getInfo() {
return getUsername() + " - " + getEmail() + " (" + getPostCount() + " posts)";
}
}
// Custom query with projection
@Query("SELECT u.username as username, u.email as email, COUNT(p) as postCount " +
"FROM User u LEFT JOIN u.posts p GROUP BY u.id")
Page<UserSummary> findUserSummaries(Pageable pageable);
4.3 Pagination with Native Queries
Native Query Pagination:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = """
SELECT u.*,
(SELECT COUNT(*) FROM posts p WHERE p.user_id = u.id) as post_count
FROM users u
WHERE u.active = true
AND (:username IS NULL OR u.username LIKE CONCAT('%', :username, '%'))
ORDER BY u.created_at DESC
""",
countQuery = """
SELECT COUNT(*) FROM users u
WHERE u.active = true
AND (:username IS NULL OR u.username LIKE CONCAT('%', :username, '%'))
""",
nativeQuery = true)
Page<Object[]> findActiveUsersWithPostCount(
@Param("username") String username,
Pageable pageable
);
// Using EntityManager for complex native queries
@PersistenceContext
private EntityManager entityManager;
public Page<User> findComplexNativeQuery(Pageable pageable) {
String sql = "SELECT * FROM users u WHERE u.active = true";
String countSql = "SELECT COUNT(*) FROM users u WHERE u.active = true";
Query query = entityManager.createNativeQuery(sql, User.class);
Query countQuery = entityManager.createNativeQuery(countSql);
// Apply pagination
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
List<User> content = query.getResultList();
Long total = ((Number) countQuery.getSingleResult()).longValue();
return new PageImpl<>(content, pageable, total);
}
}
5. Global Pagination Configuration
5.1 Custom Pageable Handler
Pageable Configuration:
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setOneIndexedParameters(true); // Use 1-based page indexing
resolver.setMaxPageSize(100); // Maximum page size
resolver.setFallbackPageable(PageRequest.of(0, 20)); // Default pagination
argumentResolvers.add(resolver);
}
}
5.2 Custom Page Serializer
Custom JSON Serializer for Page:
@Component
public class PageSerializer extends JsonSerializer<Page<?>> {
@Override
public void serialize(Page<?> page, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeStartObject();
// Content
gen.writeObjectField("content", page.getContent());
// Pagination info
gen.writeNumberField("pageNumber", page.getNumber());
gen.writeNumberField("pageSize", page.getSize());
gen.writeNumberField("totalElements", page.getTotalElements());
gen.writeNumberField("totalPages", page.getTotalPages());
// Navigation
gen.writeBooleanField("first", page.isFirst());
gen.writeBooleanField("last", page.isLast());
gen.writeBooleanField("empty", page.isEmpty());
gen.writeNumberField("numberOfElements", page.getNumberOfElements());
// Links
if (page.hasPrevious()) {
gen.writeNumberField("previousPage", page.getNumber() - 1);
}
if (page.hasNext()) {
gen.writeNumberField("nextPage", page.getNumber() + 1);
}
gen.writeEndObject();
}
}
6. Testing Pagination
6.1 Repository Tests
Pagination Repository Test:
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource(properties = {
"spring.jpa.show-sql=true",
"spring.jpa.properties.hibernate.format_sql=true"
})
class UserRepositoryPaginationTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@BeforeEach
void setUp() {
// Create test data
for (int i = 1; i <= 25; i++) {
User user = new User("user" + i, "user" + i + "@example.com");
user.setActive(i % 2 == 0); // Every second user is active
entityManager.persist(user);
}
entityManager.flush();
}
@Test
void whenFindAllWithPagination_thenReturnFirstPage() {
// Given
Pageable pageable = PageRequest.of(0, 10);
// When
Page<User> result = userRepository.findAll(pageable);
// Then
assertThat(result.getContent()).hasSize(10);
assertThat(result.getNumber()).isEqualTo(0);
assertThat(result.getTotalElements()).isEqualTo(25);
assertThat(result.getTotalPages()).isEqualTo(3);
assertThat(result.isFirst()).isTrue();
assertThat(result.hasNext()).isTrue();
}
@Test
void whenFindAllWithPagination_thenReturnSecondPage() {
// Given
Pageable pageable = PageRequest.of(1, 10);
// When
Page<User> result = userRepository.findAll(pageable);
// Then
assertThat(result.getContent()).hasSize(10);
assertThat(result.getNumber()).isEqualTo(1);
assertThat(result.isFirst()).isFalse();
assertThat(result.hasNext()).isTrue();
}
@Test
void whenFindActiveUsersWithPagination_thenReturnOnlyActiveUsers() {
// Given
Pageable pageable = PageRequest.of(0, 10);
// When
Page<User> result = userRepository.findByActiveTrue(pageable);
// Then
assertThat(result.getContent()).allMatch(User::isActive);
assertThat(result.getTotalElements()).isEqualTo(12); // 12 active users out of 25
}
@Test
void whenSearchUsersWithPagination_thenReturnFilteredResults() {
// Given
Pageable pageable = PageRequest.of(0, 5);
// When
Page<User> result = userRepository.findByUsernameContainingIgnoreCase("user1", pageable);
// Then
assertThat(result.getContent()).hasSize(5);
assertThat(result.getContent())
.extracting(User::getUsername)
.allMatch(username -> username.contains("user1"));
}
}
6.2 Service Tests
Pagination Service Test:
@ExtendWith(MockitoExtension.class)
class UserServicePaginationTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void whenGetAllUsersWithPagination_thenReturnPagedResult() {
// Given
Pageable pageable = PageRequest.of(0, 10);
List<User> users = createMockUsers(10);
Page<User> expectedPage = new PageImpl<>(users, pageable, 25);
when(userRepository.findAll(pageable)).thenReturn(expectedPage);
// When
Page<User> result = userService.getAllUsers(pageable);
// Then
assertThat(result).isEqualTo(expectedPage);
assertThat(result.getContent()).hasSize(10);
assertThat(result.getTotalElements()).isEqualTo(25);
verify(userRepository).findAll(pageable);
}
@Test
void whenSearchUsersWithCriteria_thenReturnFilteredPagedResult() {
// Given
Pageable pageable = PageRequest.of(0, 5);
UserSearchCriteria criteria = new UserSearchCriteria("john", null, true);
List<User> users = createMockUsers(5);
Page<User> expectedPage = new PageImpl<>(users, pageable, 15);
when(userRepository.findUsersByCriteria("john", null, true, pageable))
.thenReturn(expectedPage);
// When
Page<User> result = userService.searchUsers(criteria, pageable);
// Then
assertThat(result.getContent()).hasSize(5);
assertThat(result.getTotalElements()).isEqualTo(15);
verify(userRepository).findUsersByCriteria("john", null, true, pageable);
}
private List<User> createMockUsers(int count) {
List<User> users = new ArrayList<>();
for (int i = 1; i <= count; i++) {
users.add(new User((long) i, "user" + i, "user" + i + "@example.com"));
}
return users;
}
}
6.3 Controller Tests
Pagination Controller Test:
@WebMvcTest(UserController.class)
@Import({UserService.class, PaginationUtils.class})
class UserControllerPaginationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserRepository userRepository;
@Test
void whenGetAllUsers_thenReturnPagedResponse() throws Exception {
// Given
Pageable pageable = PageRequest.of(0, 10);
List<User> users = createMockUsers(10);
Page<User> userPage = new PageImpl<>(users, pageable, 25);
when(userRepository.findAll(pageable)).thenReturn(userPage);
// When & Then
mockMvc.perform(get("/api/users")
.param("page", "0")
.param("size", "10")
.param("sortBy", "username")
.param("direction", "asc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content.length()").value(10))
.andExpect(jsonPath("$.pageNumber").value(0))
.andExpect(jsonPath("$.pageSize").value(10))
.andExpect(jsonPath("$.totalElements").value(25))
.andExpect(jsonPath("$.totalPages").value(3))
.andExpect(jsonPath("$.first").value(true))
.andExpect(jsonPath("$.last").value(false));
}
@Test
void whenSearchUsers_thenReturnFilteredPagedResponse() throws Exception {
// Given
Pageable pageable = PageRequest.of(0, 5);
List<User> users = createMockUsers(5);
Page<User> userPage = new PageImpl<>(users, pageable, 15);
when(userRepository.findUsersByCriteria("john", "example.com", true, pageable))
.thenReturn(userPage);
// When & Then
mockMvc.perform(get("/api/users/search")
.param("username", "john")
.param("email", "example.com")
.param("active", "true")
.param("page", "0")
.param("size", "5"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content.length()").value(5))
.andExpect(jsonPath("$.totalElements").value(15));
}
private List<User> createMockUsers(int count) {
List<User> users = new ArrayList<>();
for (int i = 1; i <= count; i++) {
users.add(new User((long) i, "user" + i, "user" + i + "@example.com"));
}
return users;
}
}
7. Performance Optimization
7.1 Pagination Best Practices
Configuration Properties:
# application.properties
spring.data.web.pageable.default-page-size=20
spring.data.web.pageable.max-page-size=100
spring.data.web.pageable.one-indexed-parameters=false
# JPA properties for pagination optimization
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.query.in_clause_parameter_padding=true
Comments (0)
No comments yet. Be the first to comment!