Como gerar relatórios PDF na Web com JasperReports
Aprenda a gerar e disponibilizar relatórios PDF na sua aplicação Web com iReport e JasperReports
Todo sistema Web gera algum tipo de relatório para seus clientes. Normalmente estes relatórios são gerados no formato PDF devido sua facilidade na hora de imprimí-los. No mundo Java temos algumas opções de ferramentas para geração destes relatórios, mas sem dúvida a mais conhecida e utilizada no mercado é o JasperReports e sua ferramenta gráfica para desenhar relatórios, o iReport.
Embora seja comum associarmos o iReport como a tecnologia responsável por gerar os relatórios no mundo Java, a verdade é que ela é apenas a ferramenta gráfica que te ajudará a construir e desenhar os relatórios, ela é o que chamamos de report designer. Quem de fato gera e exporta os relatórios para determinado formato é o JasperReports, na qual é conhecido como report engine.
Resumindo, usamos o iReport para desenhar o relatório e gravar sua definição em um arquivo XML, cuja extensão é .jrxml
. Depois disso, via código Java, executamos o JasperReports para compilar este JRXML, preenchê-lo com informações e, por fim, exportar o relatório em si. Ao exportar o relatório você pode definir em que formato ele será gerado, como PDF, HTML, Excel, Word, OpenOffice etc.
A maior dificuldade dos desenvolvedores não é com o iReport, pois ele é simples e fácil de aprender, mas sim com a API do JasperReports para compilar, preencher e exportar o relatório. Aliás, eu falo por mim, pois sempre tive dificuldades com JasperReports, por esse motivo resolvi escrever este post para nunca mais esquecer...
Compilando, preenchendo e exportando o relatório
Irei focar na geração do relatório em si, na API do JasperReports, e não em como desenhá-lo com o iReport. Portanto, vou levar em consideração que você desenhou o relatório e gravou sua definição em um arquivo JRXML.
IMPORTANTE: A partir da versão 5.5.0 o iReport foi substituído pelo Jaspersoft Studio. As versões antigas serão mantidas e atualizadas somente até o final deste ano de 2015. Portanto, se estiver em um novo projeto opte pelo Jaspersoft Studio.
Para gerar um relatório com JasperReports precisamos executar 3 passos essenciais. Os passos são:
- compilar o JRXML;
- preencher o relatório compilado com dados e informações;
- exportar para PDF ou outro formato;
Agora que você já sabe quais os passos tomar, vamos ao trabalho...
Com o JRXML criado, a primeira coisa que precisamos fazer é compilá-lo com a API do JasperReports. Para isso, usamos o método estático compileReportToFile
da classe JasperCompileManager, como abaixo:
String jrxml = "alunos.jrxml";
String jasper = JasperCompileManager.compileReportToFile(jrxml);
System.out.println(jasper); // alunos.jasper
O código acima irá compilar o arquivo alunos.jrxml
em um arquivo alunos.jasper
no mesmo diretório do arquivo de origem. Um arquivo .jasper
é o relatório compilado em formato binário para que o JasperReports possa preenchê-lo e exportá-lo ao final da geração. Normalmente só precisamos dele quando a aplicação está em produção.
Com o relatório compilado, agora temos que preenchê-lo com dados e informações a partir de uma conexão JDBC e parâmetros da própria aplicação. Esses parâmetros normalmente são preenchidos pelo usuário através de um formulário, como filtros de uma pesquisa. Para isso, usamos a classe JasperFillManager e seu método fillReport
:
Map<String, Object> parametros = new HashMap<>();
parametros.put("curso_id", 2020);
Connection conexao = // obtem uma conexão jdbc...
JasperPrint print = JasperFillManager.fillReport("alunos.jasper", parametros, conexao);
Geralmente o mapa de parâmetros é utilizado dentro do relatório como filtros para a consulta SQL ou algum valor estático como nome da empresa, por exemplo. Outro detalhe importante é que o método fillReport
retorna um objeto do tipo JasperPrint, que nada mais é do que o relatório preenchido de forma genérica, isto é, sem formato definido.
Após preencher o relatório nós devemos exportá-lo para um formato concreto, como PDF, HTML, Excel, entre outros. Para isso, usamos classes especiais responsáveis por exportar estes relatórios genéricos para determinado formato, elas são chamadas de exporters, ou mais especificamente JRExporters. Para o nosso caso, exportaremos o relatório para PDF, como abaixo:
OutputStream saida = new FileOutputStream("alunos.pdf");
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, saida);
exporter.exportReport(); // gera o relatório no fluxo de saída
Repare que o arquivo PDF final será gerado na saída que indicamos, nesse caso num FileOutputStream com o nome alunos.pdf.
Para facilitar a compreensão, vamos juntar todo o código em um bloco um pouco mais organizado, como a seguir:
String jrxml = "alunos.jrxml";
Map<String, Object> parametros = new HashMap<>();
Connection conexao = new ConnectionFactory().getConnection();
OutputStream saida = new FileOutputStream("alunos.pdf");
// compila jrxml em um arquivo .jasper
String jasper = JasperCompileManager.compileReportToFile(jrxml);
// preenche relatorio
JasperPrint print = JasperFillManager.fillReport(jasper, parametros, conexao);
// exporta para pdf
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, saida);
exporter.exportReport();
Embora o JasperReports já tenham alguns bons anos de mercado ele ainda possui uma documentação fraca e com poucos exemplos práticos, em especial a API de exporters. De qualquer forma, ela melhorou na sua versão 6.x:
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(new SimpleExporterInput(print));
exporter.setExporterOutput(outpuStream);
SimplePdfExporterConfiguration conf = new SimplePdfExporterConfiguration();
exporter.setConfiguration(conf);
exporter.exportReport();
Com essa nova API você tem a possibilidade de fazer ajustes finos no PDF de maneira mais simples, como ajustar fontes, layout, versão do formato exportado, criptografia, permissões, idioma e muito mais.
Caso queira o relatório em outro formato, como Excel, basta mudar o código responsável pela exportação do arquivo.
Encapsulando em uma classe
As chances são que seu sistema terá que gerar inúmeros relatórios para satisfazer a necessidade do cliente. Dessa forma, se o número de relatórios cresce, o código para gerá-los também cresce. É neste momento que você deve tomar cuidado ou você acabará com muita duplicação de código.
Para evitar cair nesse armadilha, é importante isolarmos o código da API do JasperReports em uma classe com uma responsabilidade bem definida: gerar os relatórios da aplicação. Esta classe encapsulará os detalhes da API e nos proverá alguns métodos com uma assinatura mais simples de usar. Esta classe será nosso gerador de relatório e seu código poderia ser algo a semelhante a este aqui:
public class GeradorDeRelatorios {
private Connection conexao;
public GeradorDeRelatorios(Connection conexao) {
this.conexao = conexao;
}
public void geraPdf(String jrxml,
Map<String, Object> parametros, OutputStream saida) {
try {
// compila jrxml em memoria
JasperReport jasper = JasperCompileManager.compileReport(jrxml);
// preenche relatorio
JasperPrint print = JasperFillManager.fillReport(jasper, parametros, this.conexao);
// exporta para pdf
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, saida);
exporter.exportReport();
} catch (Exception e) {
throw new RuntimeException("Erro ao gerar relatório", e);
}
}
}
O código acima não difere do que vimos anteriormente, exceto que em vez de compilar o arquivo .jrxml
em um arquivo físico .jasper
nós estamos compilando-o na memória, dessa forma não temos que nos preocupar onde ele será gerado ou como mantê-lo sincronizado durante o desenvolvimento:
// compila jrxml em memoria
JasperReport jasper = JasperCompileManager.compileReport(jrxml);
Costumo seguir esta abordagem em meus projetos pois não me preocupo em pré-compilar os JRXMLs e empacotá-los dentro do .war
. No fim tenho apenas os .jrxml
na aplicação, nada de .jasper
!
Por fim, para usar nossa classe bastaria um código como este:
GeradorDeRelatorios gerador = new GeradorDeRelatorios(conexao);
gerador.geraPdf("c:\alunos.jrxml", parametros, saida);
Bem melhor, não é?
Gerando relatório na Web
Uma das dúvidas mais frequentes em listas de discussão e fóruns é sobre como gerar relatórios em PDF numa aplicação Web. Gerar o relatório nós já sabemos, acabamos de fazer mais acima. O que nos falta é disponibilizar o relatório para download, dessa forma o usuário pode baixá-lo.
Em qualquer aplicação Web para disponibilizar o arquivo PDF para download será preciso escrever o seu conteúdo na resposta do protocolo HTTP. Escrever o conteúdo do relatório no response é muito simples, basicamente precisamos obter o fluxo de saída do response e passá-lo para nosso gerador. O código abaixo exemplifica bem o que estou dizendo:
HttpServletResponse response = // obtém o response do seu framework mvc
OutputStream saida = response.getOutpuStream();
GeradorDeRelatorios gerador = new GeradorDeRelatorios(conexao);
gerador.geraPdf("c:\alunos.jrxml", parametros, saida);
Pronto! Este código é suficiente para enviar o PDF para o browser independente do framework MVC que você esteja usando. Por exemplo, se levarmos esse código para dentro de uma Servlet teríamos algo bem próximo disso:
@WebServlet("/relatorios/alunos")
public class RelatorioDeAlunosServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// acha jrxml dentro da aplicação
ServletContext contexto = request.getServletContext();
String jrxml = contexto.getRealPath("/relatorios/alunos.jrxml");
// prepara parâmetros
Map<String, Object> parametros = new HashMap<>();
parametros.put("curso", request.getParameter("curso_id"));
// abre conexão com o banco
Connection conexao = new ConnectionFactory().getConnection();
// gera relatório
GeradorDeRelatorios gerador = new GeradorDeRelatorios(conexao);
gerador.geraPdf(jrxml, parametros, response.getOutpuStream());
conexao.close(); // não esqueça de fechar a conexão
}
}
O relatório será gerado e exibido diretamente na janela do navegador. Caso queira forçar o download do PDF você precisará escrever um pouco mais de código a fim de definir os cabeçalhos HTTP da resposta.
Um detalhe importante é que no código acima estamos buscando o JRXML de forma dinâmica dentro da aplicação. Isso se faz necessário pois normalmente os .jrxml
estão dentro raiz da aplicação Web e não em um local específico no sistemas de arquivos. O trecho de código na qual estou falando é este:
ServletContext contexto = request.getServletContext();
String jrxml = contexto.getRealPath("/relatorios/alunos.jrxml");
Perceba que o código está simples por questões de didática, porém é sua responsabilidade garantir que a conexão será fechada num bloco try-catch-finally
, assim você evita problemas de vazamento de conexão na sua aplicação.
E as libs?
Não adianta mostrar só o código, né? Precisamos saber quais as bibliotecas (JARs) necessários para rodar nosso código com JasperReports. Basicamente você precisará dos seguintes JARs dentro do seu projeto:
- jasperreports-5.x.x.jar
- iText-2.x.x.jar (necessária para gerar o PDF em si)
- jfreechart-1.x.x.jar (necessária se você quiser gerar gráficos e charts)
- jcommon-1.x.x.jar (necessária se você quiser gerar gráficos e charts)
- commons-beanutils-1.x.x.jar
- commons-collections-3.x.x.jar
- commons-digester-2.x.jar
- commons-logging-1.x.jar
Para não se confundir com as versões dos JARs, eu recomendo você copiar todas as bibliotecas acima do iReport (diretório lib
de instalação) que você esteja usando para desenhar seus relatórios, dessa maneira você garante a compatibilidade da engine de geração do iReport com sua aplicação Web.
Ou caso você use Maven:
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>5.5.0</version>
</dependency>
As bibliotecas listadas são para gerar relatórios em PDF, outros formatos podem requerer mais alguns JARs.
Concluindo
Gerar relatórios com JasperReports não é uma das tarefas mais complicadas para um desenvolvedor, no entanto é comum haver alguma dificuldade devido a falta de documentação para iniciantes e exemplos práticos. Eu sempre tive certa dificuldade de configurar a ferramenta a cada novo projeto, por isso esse artigo também servirá para o Rafael Ponte do futuro.
Além disso, gerar relatórios na Web é tão simples quanto gerá-los em um método main
qualquer. Com um pouquinho de conhecimento sobre HTTP e Servlet fica fácil disponibilizar o arquivo para download com qualquer framework MVC.
Enfim, quando o assunto é relatórios a dupla iReport e JasperReports são certamente as ferramentas opensource líderes de mercado. As chances são de que você terá que aprendê-las para seu próximo projeto ou empresa. Dominar somente o iReport não é suficiente caso você tenha que implementar seu próprio gerador ou fazer ajustes finos no relatório antes exportá-lo para PDF ou outro formato.
E aí, você usa iReport e JasperReports no seu projeto? Como você implementou seu gerador de relatórios?
Desenvolvedor e instrutor na TriadWorks