Lazy loading, search and sorting in the database
grid.setItems(service.findAll()) loads all rows into memory at once. That is fine for three posts, not for a hundred thousand. Lazy loading solves it: the grid asks the server only for the rows visible on screen, 50 at a time. As the user scrolls, it requests the next batch.
Queries in the repository and service
The grid needs two queries: "give me rows N to M" and "how many rows are there in total". Let us add them with a title filter:
// src/main/java/com/example/blog/PostRepository.java
package com.example.blog;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByTitleContainingIgnoreCase(String title, Pageable pageable);
long countByTitleContainingIgnoreCase(String title);
}Pageable holds the page number, its size and the sort order. Spring Data turns the method name into SQL with LIKE, LIMIT and ORDER BY. Add to PostService:
// src/main/java/com/example/blog/PostService.java (new methods)
public List<Post> find(String filter, Pageable pageable) {
return repository.findByTitleContainingIgnoreCase(filter, pageable);
}
public long count(String filter) {
return repository.countByTitleContainingIgnoreCase(filter);
}You need the import org.springframework.data.domain.Pageable.
A grid with lazy loading
// src/main/java/com/example/blog/views/PostListView.java
package com.example.blog.views;
import com.example.blog.Post;
import com.example.blog.PostService;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
@Route("")
@PageTitle("Posts")
public class PostListView extends VerticalLayout {
private final Grid<Post> grid = new Grid<>(Post.class, false);
private final TextField filter = new TextField();
public PostListView(PostService service) {
filter.setPlaceholder("Search by title");
filter.setClearButtonVisible(true);
filter.setValueChangeMode(ValueChangeMode.LAZY);
filter.addValueChangeListener(event -> grid.getDataProvider().refreshAll());
grid.addColumn(Post::getTitle).setHeader("Title").setSortProperty("title");
grid.addColumn(Post::getAuthor).setHeader("Author").setSortProperty("author");
grid.addColumn(Post::getPublishedOn).setHeader("Date").setSortProperty("publishedOn");
grid.setItems(
query -> service.find(filter.getValue(),
VaadinSpringDataHelpers.toSpringPageRequest(query)).stream(),
query -> (int) service.count(filter.getValue()));
add(filter, grid);
setSizeFull();
}
}The first lambda gets a query with the offset, batch size and sort order and returns a stream of rows. VaadinSpringDataHelpers.toSpringPageRequest turns that query into a Pageable for Spring Data. The second lambda returns the total row count so the scrollbar has the right length.
Sorting and the filter
setSortProperty("title") makes the column sortable and says which entity field to sort by in the database. On a header click the grid runs the query again, and the SQL gets ORDER BY title.
Type "grid" into the search field: half a second after you pause, the grid keeps one row, "Grids and forms". refreshAll() makes the grid run both queries again with the new filter value.
How many rows are loaded
| Setting | Default |
|---|---|
Batch size (setPageSize) | 50 rows |
| When the next batch loads | When scrolling reaches the end of loaded rows |
| What the server keeps | Only the visible part in memory |
Common mistake
The first lambda ignores query and returns all rows:
// src/main/java/com/example/blog/views/PostListView.java (wrong)
grid.setItems(query -> service.findAll().stream(), query -> service.findAll().size());Vaadin notices the broken contract and the view fails:
java.lang.IllegalStateException: The data provider hasn't ever called getLimit() or getPageSize() method on the provided query. It means that the the data provider breaks the contract and the returned stream contains unexpected data.Always pass the offset and size from query to the database — the easiest way is toSpringPageRequest(query). If the name in setSortProperty does not match an entity field, Spring Data answers with No property 'date' found for type 'Post'.