Handling Large Datasets with JHipster Server-Side Pagination
Learn how JHipster uses Spring Data JPA's Pageable interface to implement server-side pagination, preventing memory overflows and optimizing database queries for large datasets.
28 Dec 2025, 20:59 UTC

When an application grows from a few hundred records to hundreds of thousands, loading an entire table into the browser causes UI lag and potential OutOfMemoryErrors in the JVM. Many developers initially attempt client-side filtering, but this requires transferring the entire dataset over the network, which is unsustainable at scale.
The solution is to shift the data slicing to the database. By utilizing Spring Data JPA's pagination, JHipster ensures that the application only processes the specific subset of data required for the current view, maintaining a lean memory footprint regardless of total table size.
The Pageable Architecture
JHipster implements pagination using the Pageable interface from Spring Data. When an entity is generated, JHipster creates a repository that extends JpaRepository (or PagingAndSortingRepository), which provides built-in support for paginated queries.
The data flow follows this sequence:
- The frontend sends a GET request with query parameters:
page(zero-indexed),size(records per page), andsort(property and direction). - The Spring Controller maps these parameters into a
PageRequestobject. - Hibernate translates this request into SQL using
LIMITandOFFSETclauses. - The database returns a
Page<T>object containing the requested slice of data and metadata about the total record count.
Implementation Example: The Resource Layer
In a standard JHipster-generated Resource class, the controller is designed to accept a Pageable argument directly. This removes the need to manually parse strings or calculate offsets in the business logic.
// Example in ProductResource.java
@GetMapping
public ResponseEntity<Page<ProductDTO>> getAllProducts(Pageable pageable) {
// The service layer passes the pageable object to the JpaRepository
Page<ProductDTO> page = productService.findAll(pageable);
return ResponseEntity.ok(page);
}
To verify this behavior, you can execute a request using curl from your terminal. Ensure your application is running on port 8080:
curl "http://localhost:8080/api/products?page=0&size=10&sort=name,asc"
The resulting JSON response provides the necessary metadata for the frontend to render pagination controls:
{
"content": [ { "id": 1, "name": "Widget A" }, ... ],
"totalElements": 500,
"totalPages": 50,
"size": 10,
"number": 0,
"first": true,
"last": false
}
Performance Trade-offs and Limitations
While server-side pagination is essential, it introduces specific database overheads that can impact performance if not managed:
- The Offset Penalty: In many SQL dialects, requesting a high page number (e.g.,
page=10000) forces the database to scan through all preceding rows before returning the requested slice. This "deep paging" can lead to linear degradation in response times. - Count Query Overhead: To calculate
totalPages, Spring Data JPA executes aSELECT COUNT(*)query. On tables with millions of rows or complex joins, this count operation can become a bottleneck. - Sorting Costs: Sorting on non-indexed columns forces the database to perform a full table scan and a manual sort in memory.
Verification and Diagnostics
To confirm that pagination is happening at the database level rather than in the application memory, enable Hibernate SQL logging in your application.yml:
spring:
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
When you trigger a paginated request, inspect the console logs. You should see a SELECT statement appended with LIMIT ? OFFSET ?. If these clauses are missing, the application is likely loading the full result set into memory before slicing it, which indicates a configuration error in the repository or service layer.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.