Сегодня возился с одним отчётом, который фактически состоит из двух других полноценных отчётов. Я сделал один отчёт-контейнер, в котором две секции Details. В каждой из секций разместил элемент <subreport>. Элемент <subreport> указывает на скомпилированный шаблон отчёта с помощью элемента <subreportExpression>, в котором можно указать адрес скомпилированного шаблона ( *.jasper ). В моём проекте компиляция шаблонов происходила в память, файлы не создавались. Я решил не менять пока текущую структуру генерации отчётов, принятую в проекте.
Сначала я сделал передачу скомпилированных отчётов в параметры отчёта, а в самом шаблоне-контейнере указывал так:
1 2 3 4 5 6 7 8 9 10 |
<subreport> <reportElement stretchType="RelativeToBandHeight" x="0" y="0" width="800" height="236" uuid="9e16152f-274d-413e-851d-21a6c8c554c9"/> <subreportParameter name="orgId"> <subreportParameterExpression><![CDATA[$P{orgId}]]></subreportParameterExpression> </subreportParameter> <!-- ещё параметры --> <connectionExpression><![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression> <subreportExpression><![CDATA[$P{operations_cost_report}]]></subreportExpression> </subreport> |
Обратите внимание на <subreportExpression>. Там используется параметр отчёта, то есть в шаблоне должен быть объявлен указанный параметр operations_cost_report. Вот так:
1 |
<parameter name="operations_cost_report" class="net.sf.jasperreports.engine.JasperReport" isForPrompting="false"/> |
При генерации отчёта в качестве аргумента в этот параметр нужно передавать скомпилированный шаблон, то есть класс net.sf.jasperreports.engine.JasperReport.
Но как сделать так, чтобы дочерние отчёты сами компилировались? В проекте, над которым я работаю все шаблоны находятся в src/main/resources/report, то есть в classpath. Поэтому я решил, что будут давать параметру имя, равное имени файла с шаблоном, но без расширения. Например, в случае когда файл шаблона имеет имя operations_cost_report.jrxml, параметр будет иметь имя operations_cost_report. Теперь если посмотреть все элементы <subreport> в шаблоне, то можно узнать имена файлов, которые нужно скомпилировать и передать в качестве аргументов. Код компиляции шаблона с компиляцией всех подшаблонов по описанному выше принципу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
... /** * Компиляция шаблона. Компилируются также и подшаблоны. В подшаблонах * subreportExpression должен быть вида: "$P{subreport_file}". * subreport_file - это имя файла дочернего шаблона без расширения. * @param locale Локаль. * @param name Имя шаблона для компиляции. * @param parameters Параметры шаблона. Туда будут вставляться * скомпилированные подшаблоны. */ private JasperReport compileReport(final Locale locale, final String name, final Map<String, Object> parameters) throws JRException, IOException { // Загрузка шаблона String path = String.format("report/%s.jrxml", name); JasperReport jasperReport; try (InputStream is = PrintServiceImpl.class.getClassLoader() .getResourceAsStream(path)) { // Если нет, то выбрасываем ошибку if (is == null) { String msg = messageSource.getMessage("error.noSuchReport", new Object[] { name }, locale); LOG.error(msg); throw new DcInternalException(msg); } // Компиляция jasperReport = JasperCompileManager.compileReport(is); } // Обходим элементы скомпилированного шаблона JRElementsVisitor.visitReport(jasperReport, new JRVisitor() { @Override public void visitBreak(JRBreak breakElement) { // TODO Auto-generated method stub } @Override public void visitChart(JRChart chart) { // TODO Auto-generated method stub } @Override public void visitCrosstab(JRCrosstab crosstab) { // TODO Auto-generated method stub } @Override public void visitElementGroup(JRElementGroup elementGroup) { // TODO Auto-generated method stub } @Override public void visitEllipse(JREllipse ellipse) { // TODO Auto-generated method stub } @Override public void visitFrame(JRFrame frame) { // TODO Auto-generated method stub } @Override public void visitImage(JRImage image) { // TODO Auto-generated method stub } @Override public void visitLine(JRLine line) { // TODO Auto-generated method stub } @Override public void visitRectangle(JRRectangle rectangle) { // TODO Auto-generated method stub } @Override public void visitStaticText(JRStaticText staticText) { // TODO Auto-generated method stub } @Override public void visitSubreport(JRSubreport subreport) { // Компилируем подшаблоны. // В нашем случае subreportExpression равны // $P{имя_отчёта}, где имя_отчёта - имя файла с отчётом // без расширения. String subReportName = subreport.getExpression().getText() .trim().replaceAll("(\\$P\\{)|\\}", ""); LOG.info("subReportName={}", subReportName); try { JasperReport jasperSubreport = compileReport(locale, subReportName, parameters); parameters.put(subReportName, jasperSubreport); } catch (JRException | IOException e) { LOG.error("compile subreport {} failed.", subReportName); LOG.error("", e); } } @Override public void visitTextField(JRTextField textField) { // TODO Auto-generated method stub } @Override public void visitComponentElement( JRComponentElement componentElement) { // TODO Auto-generated method stub } @Override public void visitGenericElement(JRGenericElement element) { // TODO Auto-generated method stub } }); return jasperReport; } ... |