You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用JRBeanCollectionDataSource在Jasper Reports中合并表格行单元格?

解决Jasper报表关联单元格合并问题(JRBeanCollectionDataSource实现)

我来帮你搞定这个单元格合并的问题,用JRBeanCollectionDataSource完全可以实现你想要的效果——核心思路是先对数据进行分组处理,再结合Jasper的分组报表配置来实现单元格合并,比双数据集方案更直接高效。

第一步:重构数据模型与分组准备

首先,我们需要把原始数据按关联标识(比如你示例中的"AAA"、"CCC")分组,每个主标识对应一组详情数据。

1.1 创建分组数据实体类

先定义一个用于分组的类,用来存储主标识和对应的详情列表:

public class GroupedData {
    private String mainName; // 比如"AAA"、"CCC"
    private List<MyDataClass> details; // 对应主标识的所有详情数据

    // Getter和Setter方法
    public String getMainName() {
        return mainName;
    }

    public void setMainName(String mainName) {
        this.mainName = mainName;
    }

    public List<MyDataClass> getDetails() {
        return details;
    }

    public void setDetails(List<MyDataClass> details) {
        this.details = details;
    }
}

1.2 对原始数据进行分组

修改你的Java数据填充代码,把原始的MyDataClass列表按主标识分组,转成GroupedData列表:

List<MyDataClass> datas = new ArrayList<>();
MyDataClass data1 = new MyDataClass("AAA", "AAA details1", "AAA details2", "good", "AAA details3");
MyDataClass data2 = new MyDataClass("AAA", "AAA details11", "AAA details22", "good", "AAA details33");
MyDataClass data3 = new MyDataClass("CCC", "CCC details1", "CCC details2", "good", "AAA details3");
datas.add(data1);
datas.add(data2);
datas.add(data3);

// 按主名称分组(假设MyDataClass有getMainName()方法获取"AAA"这类字段)
Map<String, List<MyDataClass>> groupedMap = datas.stream()
        .collect(Collectors.groupingBy(MyDataClass::getMainName));

// 转换为分组后的数据集
List<GroupedData> groupedDatas = new ArrayList<>();
groupedMap.forEach((key, value) -> {
    GroupedData groupedData = new GroupedData();
    groupedData.setMainName(key);
    groupedData.setDetails(value);
    groupedDatas.add(groupedData);
});

// 创建主数据源
JRBeanCollectionDataSource mainDataSource = new JRBeanCollectionDataSource(groupedDatas);

第二步:设计Jasper报表模板

接下来在JasperSoft Studio中配置报表,实现单元格合并和详情展示:

2.1 配置主数据集

在报表的Dataset and Query面板中,设置主数据集的数据源类型为JavaBean Data Source,关联GroupedData类,导入它的字段(mainName、details)。

2.2 创建分组

  1. 右键报表根节点 → Add Group,选择Group by a field,分组字段选择$F{mainName}。
  2. 把需要合并的主标识字段(比如mainName)拖到Group Header band中,设置该单元格的属性:
    • Print Repeated Values:设为false(避免同一组重复打印主标识)
    • Stretch Type:设为Relative to Tallest Object(让单元格高度自动匹配同组详情的总高度,实现合并效果)

2.3 添加详情展示组件

在Detail band中添加一个List组件(推荐)或子报表,用来展示当前分组的详情数据:

  1. 选中List组件,在Properties面板中设置Data Source Expression为:
    new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{details})
    
  2. 在List的内部Detail band中,拖入MyDataClass的字段(比如detail1、detail2等),配置好列格式即可。

第三步:填充报表并输出

最后调整Java代码,传递主数据源并填充报表:

Map<String, Object> parameters = new HashMap<>();

// 加载并编译报表模板
JasperReport jasperReport = JasperCompileManager.compileReport("your_report_template.jrxml");
// 填充报表
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, mainDataSource);

// 输出报表(比如导出为PDF)
JasperExportManager.exportReportToPdfFile(jasperPrint, "merged_report.pdf");

为什么之前的方案会有空行?

你之前用printRepeatedValues = false时出现空行,是因为原始数据没有分组,报表的Detail band会为每一条原始数据输出一行,重复的主标识被隐藏后就留下了空行。而分组方案把主标识放在Group Header,详情放在子List里,每一组只会输出一个主标识行,对应所有详情,自然不会有空行。

可选:双数据集方案(如果坚持使用)

如果你一定要用双数据集,也可以这样做:

  1. 第一个数据集是去重后的主数据(比如只包含AAA、CCC的列表),第二个数据集是完整的详情数据。
  2. 在报表中,主数据集作为报表数据源,在Detail band中为子报表/List设置Filter Expression:$F{mainName}.equals($P{CURRENT_MAIN_NAME}),其中CURRENT_MAIN_NAME是主数据集当前行的主标识。
  3. 这种方式需要额外传递详情数据集作为参数,效率不如分组方案,因为每次都要过滤整个详情数据集。

内容的提问来源于stack exchange,提问作者Sashok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 03:52:35