Java 如何在 Spring MVC 中使用 JasperReports?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27532446/
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-08-11 04:31:58  来源:igfitidea点击:

How to use JasperReports with Spring MVC?

javaspringspring-mvcjasper-reportsspring-4

提问by Brice Roncace

I've been investigating the use of JasperReports (6.0.0) with Spring MVC (4.1.3) to generate PDF reports. Spring is rife with "Spring specific" ways to integrate with JasperReports to generate PDFs:

我一直在研究使用 JasperReports (6.0.0) 和 Spring MVC (4.1.3) 来生成 PDF 报告。Spring 充斥着与 JasperReports 集成以生成 PDF 的“Spring 特定”方式:

I struggled to find good, complete examples online and wanted to share my findings (see my answer below).

我努力在网上找到好的、完整的例子,并想分享我的发现(见下面我的回答)。

Feel free to add additional methods and/or improvements related to "How can I integrate JasperReports with Spring4"?

随意添加与“如何将 JasperReports 与 Spring4 集成”相关的其他方法和/或改进?

采纳答案by Brice Roncace

Based on my research, I've found the following usage methods. The methods begin with the most direct (naive) approach involving less up front complexity / configuration and evolve to become more abstract but with more dependencies on Spring / more complex Spring configuration.

根据我的研究,我发现了以下使用方法。这些方法从最直接(天真)的方法开始,涉及较少的前期复杂性/配置,然后演变为更抽象但对 Spring 的依赖更多/更复杂的 Spring 配置。

Method 1: Use the JasperReports API directly in the Controller

方法一:在Controller中直接使用JasperReports API

Just write out the content to the servlet output stream.

只需将内容写出到 servlet 输出流。

  @RequestMapping(value = "helloReport1", method = RequestMethod.GET)
  @ResponseBody
  public void getRpt1(HttpServletResponse response) throws JRException, IOException {
    InputStream jasperStream = this.getClass().getResourceAsStream("/jasperreports/HelloWorld1.jasper");
    Map<String,Object> params = new HashMap<>();
    JasperReport jasperReport = (JasperReport) JRLoader.loadObject(jasperStream);
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource());

    response.setContentType("application/x-pdf");
    response.setHeader("Content-disposition", "inline; filename=helloWorldReport.pdf");

    final OutputStream outStream = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint, outStream);
  }


Method 2: Inject JasperReportPdf View into Controller

方法二:将 JasperReportPdf 视图注入控制器

Given the JasperReportsPdfView bean:

鉴于 JasperReportsPdfView bean:

@Bean @Qualifier("helloWorldReport2")
public JasperReportsPdfView getHelloWorldReport() {
  JasperReportsPdfView v = new JasperReportsPdfView();
  v.setUrl("classpath:jasperreports/HelloWorld2.jasper");
  v.setReportDataKey("datasource");
  return v;
}

This view can be injected or wired into the Controller for use:

此视图可以注入或连接到控制器中以供使用:

@Autowired @Qualifier("helloWorldReport2")
private JasperReportsPdfView helloReport;

@RequestMapping(value = "helloReport2", method = RequestMethod.GET)
public ModelAndView getRpt2(ModelAndView modelAndView) {
  Map<String, Object> parameterMap = new HashMap<>();
  parameterMap.put("datasource", new JREmptyDataSource());
  modelAndView = new ModelAndView(helloReport, parameterMap);
  return modelAndView;
}

Note that using the JasperReportsPdfView(or the more versatile JasperReportsMultiFormatView) requires a dependency on spring-context-support:

请注意,使用JasperReportsPdfView(或更通用的JasperReportsMultiFormatView)需要依赖于 spring-context-support:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>4.1.3</version>
</dependency>


Method 3: Use XML or ResourceBundle view resolver to map logical view names to JasperReport views

方法 3:使用 XML 或 ResourceBundle 视图解析器将逻辑视图名称映射到 JasperReport 视图

Configure a new view resolver, in this case the ResourceBundleViewResolverto run before the InternalResourceViewResolver. This is based on the order values being set (0 happens before 1):

配置一个新的视图解析器,在这种情况下,ResourceBundleViewResolverInternalResourceViewResolver. 这是基于设置的订单值(0 发生在 1 之前):

@Bean
public ResourceBundleViewResolver getResourceBundleViewResolver() {
  ResourceBundleViewResolver resolver = new ResourceBundleViewResolver();
  resolver.setBasename("jasperreport-views");
  resolver.setOrder(0);
  return resolver;
}

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setOrder(1);
  return resolver;
}

Then, at the root of our classpath, the jasperreport-views.propertiesfile can contain the logical view name paired with the class and property values (i.e. url and reportDataKey) pertinent to rending a JasperReport:

然后,在我们的类路径的根目录下,该jasperreport-views.properties文件可以包含与渲染 JasperReport 相关的类和属性值(即 url 和 reportDataKey)配对的逻辑视图名称:

helloReport3.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
helloReport3.url=classpath:/jasperreports/HelloWorld3.jasper
helloReport3.reportDataKey=myDataSourceKey

The controller code looks like this:

控制器代码如下所示:

@RequestMapping(value = "helloReport3", method = RequestMethod.GET)
public ModelAndView getRpt3(ModelMap modelMap, ModelAndView modelAndView) {
  modelMap.put("myDataSourceKey", new JREmptyDataSource());
  return new ModelAndView("helloReport3", modelMap);
}

I like this approach. Controllers stay "dumb" and only deal with String values and the mapping of names to views can happen all in one location.

我喜欢这种方法。控制器保持“愚蠢”,只处理字符串值,名称到视图的映射可以在一个位置发生。



Method 4: Use JasperReportsViewResolver

方法四:使用 JasperReportsViewResolver

Configure a zero-ordered JasperReportViewResolverand the trick is use setViewNamesto tell Spring which logical view names you want this resolver to deal with (otherwise you end up with "Could not load JasperReports report from class path resource [jasperreports/index.jasper]" type errors):

配置一个零序JasperReportViewResolver,技巧是setViewNames用来告诉 Spring 你希望这个解析器处理哪些逻辑视图名称(否则你最终会得到“无法从类路径资源 [jasperreports/index.jasper] 加载 JasperReports 报告”类型错误):

@Bean
public JasperReportsViewResolver getJasperReportsViewResolver() {
  JasperReportsViewResolver resolver = new JasperReportsViewResolver();
  resolver.setPrefix("classpath:/jasperreports/");
  resolver.setSuffix(".jasper");
  resolver.setReportDataKey("datasource");
  resolver.setViewNames("rpt_*");
  resolver.setViewClass(JasperReportsMultiFormatView.class);
  resolver.setOrder(0);
  return resolver;
}  

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setOrder(1);
  return resolver;
}

And inside the controller:

在控制器内部:

@RequestMapping(value = "helloReport4", method = RequestMethod.GET)
public ModelAndView getRpt4(ModelMap modelMap, ModelAndView modelAndView) {
  modelMap.put("datasource", getWidgets());
  modelMap.put("format", "pdf");
  modelAndView = new ModelAndView("rpt_HelloWorld", modelMap);
  return modelAndView;
}

This is my preferred approach. Controllers resolve jasper reports in a very similar fashion to how jsp views are resolved using the InternalResourceViewResolverand there is therefore no need for an explicit mapping file as with the xml or properties file approach in method #3 above.

这是我的首选方法。控制器解析 jasper 报告的方式与使用 解析 jsp 视图的方式非常相似,InternalResourceViewResolver因此不需要像上面方法#3 中的 xml 或属性文件方法那样使用显式映射文件。

EDIT

编辑

The javadocs for JasperReportsPdfViewmention it uses the deprecated JRExporterAPI. Is there a better (newer) JasperReports view to use? Perhaps opting for the JasperReportsMultiFormatViewis a better option as it does not appear to use JRExporter.

提及它的javadocsJasperReportsPdfView使用了已弃用的JRExporterAPI。是否有更好(更新)的 JasperReports 视图可供使用?也许选择JasperReportsMultiFormatView是更好的选择,因为它似乎没有使用JRExporter.

回答by Aleksa Knezevic

My Method:

我的方法:

   @RequestMapping(value="getPDF", method=RequestMethod.GET)
   public void generatePDF(int idPredstave, HttpServletResponse response) throws Exception{

    Predstava p = pr.findById(idPredstave).get();
    List<Uloga> uloge = ur.findByPredstava(p);

    response.setContentType("text/html");
    JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(uloge);
    InputStream inputStream = this.getClass().getResourceAsStream("/jasperreports/Uloge.jrxml");
    JasperReport jasperReport = JasperCompileManager.compileReport(inputStream);
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("nazivPredstave", p.getNaziv());
    params.put("trajanje", p.getTrajanje());
    params.put("opis", p.getOpis());
    params.put("zanr", p.getZanr().getNaziv());
    params.put("reziser", p.getReziser().getIme()+" "+p.getReziser().getPrezime());
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, dataSource);
    inputStream.close();


    response.setContentType("application/x-download");
    response.addHeader("Content-disposition", "attachment; filename=Uloge.pdf");
    OutputStream out = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint,out);
}