
本文介绍如何避免 vaadin grid 在上下滚动时反复触发数据库查询,通过合理使用全量加载或自定义缓存机制,彻底消除冗余后端调用。
Vaadin Grid 默认的懒加载(setItems(BackendDataProvider))会在视口滚动到新页时动态发起分页请求——这虽节省内存,但当用户反复上下滚动时,同一页面可能被多次加载(如从第5页滚回第2页),导致 getData() 被重复调用,加重数据库负担。
✅ 推荐方案一:全量加载(适用于数据量可控场景)
若业务允许且总数据量适中(例如 ≤ 10,000 条),最简洁可靠的解法是一次性加载全部数据并交由 Grid 自行虚拟滚动管理:
Gridgrid = new Grid<>(); grid.setItems(getAllItems()); // 返回 Stream ,如 repository.findAll().stream() // 可选:启用客户端分页优化(Grid 自动处理) grid.setPageSize(50); // 仅影响渲染批次,不触发后端请求
✅ 优势:零重复请求、代码极简、响应迅速;
⚠️ 注意:需评估内存与首屏加载时间,避免 OOM 或长时间白屏。
✅ 推荐方案二:带本地缓存的懒加载(适用于大数据量场景)
当数据总量过大(如百万级)无法全量加载时,应在 getData() 中实现LRU 缓存或 Map 缓存,确保相同页码只查一次:
private final Map> cache = Collections.synchronizedMap(new LinkedHashMap<>() { @Override protected boolean removeEldestEntry(Map.Entry > eldest) { return size() > 20; // 限制最多缓存20页 } }); private static class PageKey { final int page; final int pageSize; final String id; PageKey(String id, int page, int pageSize) { this.id = id; this.page = page; this.pageSize = pageSize; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PageKey pageKey = (PageKey) o; return page == pageKey.page && pageSize == pageKey.pageSize && Objects.equals(id, pageKey.id); } @Override public int hashCode() { return Objects.hash(id, page, pageSize); } } private List getData(String id, int page, int pageSize) { PageKey key = new PageKey(id, page, pageSize); return cache.computeIfAbsent(key, k -> repository.findPaginatedById(id, page, pageSize) // 实际 DB 查询 ); } // 绑定 Grid grid.setItems(query -> getData(id, query.getPage(), query.getPageSize()).stream());
✅ 优势:兼顾性能与内存,避免重复 DB 查询;
⚠️ 注意:需考虑缓存一致性(如后台数据变更时主动清理相关 key)、线程安全(已用 synchronizedMap 示例)及过期策略。
? 总结建议
- 优先评估数据规模:若 stream) 是最佳实践;
- 禁用“无状态”懒加载陷阱:避免将 query -> getData(...).stream() 作为纯函数式提供者——它不具备记忆性;
- 切勿依赖 Grid 内部缓存:Vaadin Grid 本身不缓存服务端响应,所有分页请求均视为独立调用;
- 进阶可选:结合 InMemoryDataProvider 或 ListDataProvider 封装缓存逻辑,提升复用性与测试性。
通过以上任一方式,即可彻底杜绝滚动过程中的重复数据库调用,显著提升应用性能与用户体验。










