java 如何使用 Jasper 加载子报表资源?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4825726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 08:12:51  来源:igfitidea点击:

How to load subreport resources with Jasper?

javajasper-reports

提问by Istao

With Jasper, I use resources to load the report. So, to load the main report, I use something like :

对于 Jasper,我使用资源来加载报告。所以,为了加载主报告,我使用了类似的东西:

InputStream is = getClass().getResourceAsStream("/resources/report1.jrxml");
design = JRXmlLoader.load(is);

But, if there is a subreport in report1.jrxml, how to say it is in /resources/sub.jrxml?

但是,如果 report1.jrxml 中有一个子报表,如何说它在/resources/sub.jrxml 中

回答by lkdg

I did it this way:

我是这样做的:

jasperDesign = JRXmlLoader.load(rootpath + "/WEB-INF/templates/Report.jrxml");
jasperDesignSR = JRXmlLoader.load(rootpath + "/WEB-INF/templates/SubReport.jrxml");


JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperReport jasperReportSR = JasperCompileManager.compileReport(jasperDesignSR);

parameters.put("SubReportParam", jasperReportSR);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);

"SubReportParam" would be a parameter of the type "JasperReport" as a SubreportExpression within your Report.

“SubReportParam”将是“JasperReport”类型的参数,作为报告中的 SubreportExpression。

In the .jrxml:

在 .jrxml 中:

<parameter name="SubReportParam" class="net.sf.jasperreports.engine.JasperReport" isForPrompting="false"/>

I don't know if You use IReport for your Design of Reports. With a right click on your subreport you should find the SubreportExpression. parameters is a map which I pass to "fillReport"

我不知道您是否使用 IReport 进行报表设计。右键单击您的子报表,您应该会找到 SubreportExpression。参数是我传递给“fillReport”的地图

Good luck.

祝你好运。

回答by yankee

I was not quite happy with lkdg's answer because I wanted to separate the concern of loading the correct file from the design as in my opinion I should not be forced to organize from where the reports are loaded at design time of the JRXML files.

我对 lkdg 的回答不太满意,因为我想将加载正确文件的问题与设计分开,因为在我看来,我不应该被迫在 JRXML 文件的设计时从加载报告的位置进行组织。

Unfortunately the code of the Jasper Library is full of static references that make it hard to find the correct spot for the injection of a custom subreport loader and also some of the documentation sucks (e.g. the interface RepositoryServiceis completely lacking a contract documentation so I needed to guess the contract by reading calling code), but it is possible:

不幸的是,Jasper 库的代码充满了静态引用,这使得很难找到注入自定义子报表加载器的正确位置,而且一些文档很糟糕(例如,接口RepositoryService完全缺乏合同文档,所以我需要通过阅读调用代码来猜测合约),但有可能:

private static void fillReport() throws IOException, JRException {
    // The master report can be loaded just like that, because the
    // subreports will not be loaded at this point, but later when
    // report is filled.
    final JasperReport report = loadReport("masterReport.jasper");

    // The SimpleJasperReportsContext allows us to easily specify some
    // own extensions that can be injected into the fill manager. This
    // class will also delegate to the DefaultJasperReportsContext and
    // combine results. Thus all the default extensions will still be available
    SimpleJasperReportsContext jasperReportsContext = new SimpleJasperReportsContext();
    jasperReportsContext.setExtensions(
        RepositoryService.class, singletonList(new SubReportFindingRepository())
    );

    final byte[] pdf = JasperExportManager.exportReportToPdf(
        JasperFillManager
            .getInstance(jasperReportsContext)
            // carefully select the correct `fill` method here and don't
            // accidentally select one of the static ones!:
            .fill(report, YOUR_PARAMS, YOUR_CONNECTION)
    );
}

private static JasperReport loadReport(final String fileName) throws IOException, JRException {
    try(InputStream in = loadReportAsStream(fileName)) {
        return (JasperReport) JRLoader.loadObject(in);
    }
}

private static InputStream loadReportAsStream(final String fileName) {
    final String resourceName = "/package/path/to/reports/" + fileName;
    final InputStream report = CurrentClass.class.getResourceAsStream(resourceName);
    if (report == null) {
        throw new RuntimeException("Report not found: " + resourceName);
    }
    return report;
}

private static class SubReportFindingRepository implements RepositoryService {


    @Override
    public Resource getResource(final String uri) {
        return null; // Means "not found". The next RepositoryService will be tried
    }

    @Override
    public void saveResource(final String uri, final Resource resource) {
        throw new UnsupportedOperationException();
    }

    @Override
    public <K extends Resource> K getResource(final String uri, final Class<K> resourceType) {
        if (!isKnownSubReport(uri)) {
            return null; // Means "not found". The next RepositoryService will be tried
        }

        final ReportResource reportResource = new ReportResource();
        try {
            reportResource.setReport(loadReport(uri));
        } catch (IOException | JRException e) {
            throw new Error(e);
        }
        return resourceType.cast(reportResource);
    }

    private static boolean isKnownSubReport(final String uri) {
        return "subReport1.jasper".equals(uri) || "subReport2.jasper".equals(uri);
    }
}

As an alternative to the local injection you can also write a global extension. As far as I got it (I did not try) this requires the creation of a jasperreports_extension.propertiesfile with class names that should be loaded which can include a custom repository to load the reports from. However in this case you completely loose the ability to work with conflicting configurations needed in different use cases.

作为本地注入的替代方案,您还可以编写全局扩展。据我所知(我没有尝试过),这需要创建一个jasperreports_extension.properties文件,其中包含应该加载的类名,其中可以包含一个自定义存储库来加载报告。但是,在这种情况下,您完全无法处理不同用例中所需的冲突配置。