iT邦幫忙

2024 iThome 鐵人賽

DAY 17
0
Software Development

Java工程師的報表入門與實作系列 第 17

JasperReports-匯出沒有DataSource的靜態報表內容

  • 分享至 

  • xImage
  •  

有時候我會遇到一些比較不一樣的需求,例如需要匯出一份沒有資料來源(DataSource)的報表,只要生成一個樣式固定的靜態報表,這種報表固然簡單,但仍有值得注意的地方。

情境:支出證明單

這種靜態報表第一個要考量的是,需不需要在一個檔案或畫面中重複顯示報表,只要記得把需要重複的地方放在Detail Band就好了

靜態內容顯示

模板1. 全部放在Title

如果報表靜態內容只顯示一次,可以將全都的元素都放到Title Band就好。

準備好後端程式碼,與之前不同的是dataSourceList是null。

// 1. 設定報表參數
Map<String, Object> parametersMap = new HashMap<>();
LocalDate date = new Date().toInstant()
        .atZone(ZoneId.systemDefault()).toLocalDate();
parametersMap.put("date", date
        .format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));

// 2. 匯出excel byte[]
byte[] bytes = null;
try {
    String reportPath = "/Report/Jasper/NoDataSourceReport.jrxml";

    bytes = ExportReportUtil.templateToExcelByte(null, reportPath, parametersMap);
} catch (Exception e) {
    throw new RuntimeException(e);
}

ExportReportUtil.java

public static byte[] templateToExcelByte(List dataSourceList, String reportPath, Map<String, Object> parametersMap) throws Exception {

    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
        // 以JasperCompileManager將jrxml模板編譯成jasper文件
        JasperReport jasperReport = JasperCompileManager
            .compileReport(ExportReportUtil.class.getResourceAsStream(reportPath));

        // 將Java集合資料來源與Jasper報表進行綁定
        JRDataSource dataSource = new JRBeanCollectionDataSource(dataSourceList);

        // 將資料填入報表
        JasperPrint print = JasperFillManager
                    .fillReport(jasperReport, parametersMap, dataSource);

        // 匯出
        SimpleXlsxReportConfiguration xlsxReportConfiguration = new SimpleXlsxReportConfiguration();
        // setDetectCellType使excel偵測這個值的型別並轉換為對應的格式
        xlsxReportConfiguration.setDetectCellType(true);

        JRXlsxExporter exporter = new JRXlsxExporter();
        exporter.setConfiguration(xlsxReportConfiguration);
        exporter.setExporterInput(new SimpleExporterInput(print));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));
        exporter.exportReport();

        return byteArrayOutputStream.toByteArray();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}

匯出後是一個正常的靜態報表。

模板2. 部分內容放在Title,部分內容放在Detail

如果標題與日期不希望重複顯示,放在Title Band,但項目與事由希望重複顯示,放在Detail Band。(我舉例而已,應該是沒這種單據)

後端程式碼與「模板1」相同,匯出後會是一個只有Title的報表。

這是因為Detail Band要重複幾行就是看有多少DataSource,如果沒有DataSource與jasper文件綁定,產生的JRDataSource是null,在正式填充到jasper文件時就不會產生Detail的內容。
要解決這種狀況有兩種方式:

  • (1)使用DataSource: 沒有DataSource就沒有Detail的內容,那給他DataSource就好啦!我們可以自己創建DataSource,隨便放入什麼都可以,有意義的只有DataSource的長度就是我們希望Detail重複顯示內容的次數,這個範例中以重複5次為例
byte[] bytes = null;
try {
    String reportPath = "/Report/Jasper/NoDataSourceReport.jrxml";
    List<Integer> noDataSourceList = new ArrayList();
    // Detail要重複5次
    for (int i = 0; i <= 5 ; i++) {
        noDataSourceList.add(i);
    }

    bytes = ExportReportUtil.templateToExcelByte(noDataSourceList, reportPath, parametersMap);
} catch (Exception e) {
    throw new RuntimeException(e);
}
  • (2)使用JREmptyDataSource: JREmptyDataSource是專門用於在沒有DataSource的情況下生成報表的類別,對於生成靜態報表或查詢結果為空時,依然需要顯示我們希望的靜態內容來說非常實用。
    與DataSource不同的只有他是要替代JRBeanCollectionDataSource,因此後端要修改的是報表生命週期的部分。如果Detail只要顯示一次,直接使用new JREmptyDataSource()不填入參數就好。
public static byte[] templateToExcelByte(List dataSourceList, String reportPath, Map<String, Object> parametersMap) throws Exception {

    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
        // 以JasperCompileManager將jrxml模板編譯成jasper文件
        JasperReport jasperReport = JasperCompileManager
            .compileReport(ExportReportUtil.class.getResourceAsStream(reportPath));

        // 將Java集合資料來源與Jasper報表進行綁定
        JRDataSource dataSource = CollectionUtils.isEmpty(dataSourceList) ? 
        // Detail要重複5次
        new JREmptyDataSource(5) : new JRBeanCollectionDataSource(dataSourceList);

        // 將資料填入報表
        JasperPrint print = JasperFillManager
                .fillReport(jasperReport, parametersMap, dataSource);

        // 匯出
        SimpleXlsxReportConfiguration xlsxReportConfiguration = new SimpleXlsxReportConfiguration();
        // setDetectCellType使excel偵測這個值的型別並轉換為對應的格式
        xlsxReportConfiguration.setDetectCellType(true);

        JRXlsxExporter exporter = new JRXlsxExporter();
        exporter.setConfiguration(xlsxReportConfiguration);
        exporter.setExporterInput(new SimpleExporterInput(print));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));
        exporter.exportReport();

        return byteArrayOutputStream.toByteArray();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}

使用其中一種方法後,匯出報表後就有看到Detail的內容了

去除JREmptyDataSource顯示的null

如果我們的情境是有Field動態的顯示報表資料,但在沒有查詢到DataSource資料的狀況下,使用JREmptyDataSource,匯出時會發現所有Field會自動顯示null。


除了在後端處理之外,Jaspersoft Studio的「Text Field」元素有一個好用的屬性:Blank When Null
選取「Text Field」之後勾選「Blank When Null」,之後不論是不是使用JREmptyDataSource造成的null都不會顯示出來了。


Reference


上一篇
JasperReports-匯出含Excel公式的報表
下一篇
JasperReports-static text、text field適應內容高度
系列文
Java工程師的報表入門與實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言