2011-01-28 7 views
5

Con Jasper, utilizzo le risorse per caricare il report. Così, per caricare il rapporto principale, io uso qualcosa di simile:Come caricare le risorse del sottoreport con Jasper?

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

Ma, se c'è un sottoreport in report1.jrxml, come a dire che è in /resources/sub.jrxml?

risposta

15

ho fatto in questo modo:

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" sarebbe un parametro del tipo "JasperReport" come SubreportExpression all'interno della vostra relazione.

Nella .jrxml:

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

Non so se si utilizza IReport per la progettazione dei report. Con un clic destro sul sottoreport dovresti trovare SubreportExpression. I parametri sono una mappa che passo a "fillReport"

Buona fortuna.

3

Non ero abbastanza soddisfatto della risposta di lkdg perché volevo separare la preoccupazione di caricare il file corretto dal progetto poiché a mio avviso non dovrei essere costretto a organizzare da dove i report sono caricati in fase di progettazione del JRXML File.

Sfortunatamente il codice di Jasper Library è pieno di riferimenti statici che rendono difficile trovare il punto corretto per l'iniezione di un caricatore di un sottoreport personalizzato e anche una parte della documentazione fa schifo (ad es. L'interfaccia RepositoryService manca completamente un contratto documentazione così ho bisogno di indovinare il contratto con la lettura chiamando codice), ma è possibile:

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); 
    } 
} 

In alternativa alla iniezione locale è possibile anche scrivere una global extension. Per quanto ho ottenuto (non l'ho provato), ciò richiede la creazione di un file jasperreports_extension.properties con nomi di classi che dovrebbero essere caricati e che possono includere un repository personalizzato da cui caricare i report. Tuttavia in questo caso perdi completamente la capacità di lavorare con configurazioni in conflitto necessarie in diversi casi d'uso.

Problemi correlati