如何用Vaadin Grid创建类Excel表格并整合多SQL表数据
矩阵式Vaadin Grid实现方案(多SQL表数据源)
核心思路
Vaadin Grid是基于行数据模型的组件,要实现Excel式的矩阵异构数据展示,关键是先将分散在多表中的数据统一聚合为符合Grid行结构的模型,再通过Grid的列绑定或动态列功能完成展示。
高效实现步骤
1. 构建统一的矩阵行DTO
定义通用的MatrixRowDTO,覆盖所有行和列的可能字段,同时标记行的来源类型,方便后续区分处理:
public class MatrixRowDTO { private String rowType; // 标记行来源:"MONITORING"或"DEPLOYMENT" private Consultant consultant; // 关联顾问表数据 private BigDecimal region1Value; // 区域1数据 private BigDecimal region2Value; // 区域2数据 private BigDecimal region3Value; // 区域3数据 // 其他必要字段、getter/setter }
数据聚合建议:在DAO层通过SQL联查(如LEFT JOIN)一次性获取所有关联数据,避免多次数据库查询。示例SQL:
SELECT m.id AS row_id, 'MONITORING' AS row_type, c.name AS consultant_name, r1.value AS region1_value, r2.value AS region2_value, r3.value AS region3_value FROM Monitoring m LEFT JOIN Consultant c ON m.consultant_id = c.id LEFT JOIN Region1 r1 ON m.region1_id = r1.id LEFT JOIN Region2 r2 ON m.region2_id = r2.id LEFT JOIN Region3 r3 ON m.region3_id = r3.id UNION ALL SELECT d.id AS row_id, 'DEPLOYMENT' AS row_type, c.name AS consultant_name, r1.value AS region1_value, r2.value AS region2_value, r3.value AS region3_value FROM Deployment d LEFT JOIN Consultant c ON d.consultant_id = c.id LEFT JOIN Region1 r1 ON d.region1_id = r1.id LEFT JOIN Region2 r2 ON d.region2_id = r2.id LEFT JOIN Region3 r3 ON d.region3_id = r3.id
将查询结果直接映射到MatrixRowDTO,这是最高效的数据获取方式。
2. 配置Vaadin Grid数据提供者
如果数据量较大,使用CallbackDataProvider实现懒加载分页,避免一次性加载所有数据:
DataProvider<MatrixRowDTO, Void> dataProvider = DataProvider.fromCallbacks( query -> { // 分页查询:query.getOffset()是起始位置,query.getLimit()是每页数量 List<MatrixRowDTO> rows = matrixRowService.getRows(query.getOffset(), query.getLimit()); return rows.stream(); }, query -> matrixRowService.getTotalRowCount() // 返回总数据量 ); grid.setDataProvider(dataProvider);
小数据量则直接用ListDataProvider:
List<MatrixRowDTO> rows = matrixRowService.getAllRows(); grid.setItems(rows);
添加Areas和Consultant列
添加Consultant列
如果DTO中已关联Consultant实体,直接绑定列并渲染字段:
grid.addColumn(row -> row.getConsultant() != null ? row.getConsultant().getName() : "-") .setHeader("Consultant") .setKey("consultant"); // 设置key方便后续维护
如果DTO中仅存储consultantId,建议在数据聚合阶段就关联查询顾问名称,避免在UI渲染时发起数据库请求(会导致界面卡顿)。
添加Areas(Region 1/2/3)列
基于已有的单个DTO填充逻辑,扩展DTO字段后直接绑定:
// 静态列绑定 grid.addColumn(MatrixRowDTO::getRegion1Value) .setHeader("Region 1") .setKey("region1"); grid.addColumn(MatrixRowDTO::getRegion2Value) .setHeader("Region 2") .setKey("region2"); grid.addColumn(MatrixRowDTO::getRegion3Value) .setHeader("Region 3") .setKey("region3");
如果区域列是动态可变的(比如可能新增Region 4),可以通过循环生成动态列:
// 假设从配置或数据库获取区域列表 List<String> areaHeaders = Arrays.asList("Region 1", "Region 2", "Region 3"); // DTO中添加Map<String, BigDecimal> regionValues字段存储区域值 for (String header : areaHeaders) { grid.addColumn(row -> row.getRegionValues().getOrDefault(header, BigDecimal.ZERO)) .setHeader(header) .setKey(header.toLowerCase().replace(" ", "-")); }
优化建议
- 缓存静态数据:如果Consultant或区域字典数据不频繁更新,用本地缓存(如Guava Cache)存储,减少重复查询。
- 行样式区分:给不同来源的行添加差异化样式,提升用户体验:
对应的CSS:grid.addClassNameGenerator(row -> { return switch(row.getRowType()) { case "MONITORING" -> "monitoring-row"; case "DEPLOYMENT" -> "deployment-row"; default -> ""; }; });.monitoring-row { background-color: #f8f9fa; } .deployment-row { background-color: #fff3cd; } - 使用LazyDataView:启用懒加载后,Grid只会加载当前可见页的数据,大幅提升大数据量下的渲染速度:
LazyDataView<MatrixRowDTO> lazyDataView = grid.setLazyDataView(dataProvider); lazyDataView.setPageSize(20); // 设置每页显示行数
内容的提问来源于stack exchange,提问作者Anshaz
相关产品推荐
相关产品推荐

