Como passar a consulta dinamicamente para o JasperReports / iReport
Essa semana me deparei com um problema no trabalho que levei alguns minutos estudando a melhor solução. Eu tinha um relatório que deveria ser gerado com o JarperReports cuja consulta a ser realizada no banco poderia mudar constantemente, de acordo com o filtro que o usuário selecionasse na tela. Uma solução me veio à cabeça imediatamente: mandar um ArrayList para o JasperReports com o resultado da minha consulta para que ele preencha os dados. Essa solução é prática e fácil de implementar, mas esse ArrayList poderia ficar muito grande, visto que meu banco possuía muitas informações.
A melhor solução que encontrei foi passar a consulta inteira como parâmetro para o JasperReports fazer a consulta no banco. Para ilustrar como fazer isso, vamos aos passos abaixo:
- Definir um parâmetro do tipo String que receberá a consulta vinda da aplicação java:
Observe que temos dois parâmetros definidos por mim: Filtro - servirá para receber uma descrição textual do filtro que o usuário aplicou na geração do relatório e consulta – receberá a consulta a ser executada no banco de dados pelo JasperReports.
- Configurar a consulta do relatório como sendo o parâmetro recebido:
A consulta do relatório será exatamento o parâmetro consulta. Observe o símbolo de exclamação depois da letra P. Sem ele o iReport dá erro no momento de efetuar a compilação do relatório.
- Passar a consulta para o relatório através da aplicação Java:
private String geraRelatorioEmPdfConsulta(String consulta, String jasper, String nomeRelatorio) {
// Seto a variável saida como nulo
saida = null;
try {
JasperPrint print = JasperFillManager.fillReport(jasper, getMapComFiltroEConsulta(filtroAplicado, consulta), getNovaConexao());
saida = RelatorioUtil.getDiretorioReal("/relatorios/relatorio.pdf");
JasperExportManager.exportReportToPdfFile(print, saida);
saida = RelatorioUtil.getContextPath() + "/relatorios/relatorio.pdf";
} catch (Exception e) {
addErrorMessage("Formulario", "erro.geracao.relatorio", nomeRelatorio);
e.printStackTrace();
} finally {
fechaConexao();
}
// mostro o erro ou o relatório na janela que abre
return "geraRelatorio";
}
/*
* Esse método retorna o map com o filtro e a consulta que o usuário gerou.
*/
public Map<String, String> getMapComFiltroEConsulta(String filtroAplicado, String consulta) {
Map<String, String> map = new HashMap<String, String>();
// Se o usuário não aplicou nada no filtro, retorno nulo para o relatório
if (filtroAplicado == null)
filtroAplicado = "";
else
filtroAplicado = "Você aplicou o seguinte filtro no relatório:\n" + filtroAplicado;
map.put("Filtro", filtroAplicado);
map.put("consulta", consulta);
return map;
}
Observe, pelo código acima, que eu passo a consulta e o filtro que foram gerados para o relatório dentro de um HashMap, como o JasperReports exige. No final, o relatório com um campo do filtro fornecido ficou assim.
O campo marcado em vermelho foi o filtro aplicado pelo usuário que eu mandei para o relatório como um parâmetro chamado Filtro e que na verdade é apenas uma descrição textual.
Obs.: cada coluna retornada pela minha consulta deve ter o mesmo nome do field no relatório. Caso isso não ocorra, haverá um erro na geração.
É somente isso. Qualquer dúvida, deixe um comentário.



Tanto esse como o post anterior sobre IReport + Jasper estão bem interessantes, tem muita gente que precia utilizar geração de relátorios.
nice =)
Boa Pablo, ficou muito bom. Parabéns!
Pablo,
Muito útil sua dica! Ótimo post!
Bacana, esses posts técnicos são um canivente suiço quando procuramos soluções rápidas para problemas comuns do dia-a-dia, é uma consultoria especializada opensource
Obrigado!
Dica útil e bem explicada. Boa iniciativa!
Cara muito legal esse tutorial muito obrigado por disponibilizar seu conhecimento.
pq precisa da exclamação?
Olá Pedro. O post fala exatamente sobre isso: como passar a consulta dinamicamente para o JasperReports. Imagine que você tenha um relatório que servirá para listar os estudante de um estado, mas que também servirá para listar os estudantes que nasceram a partir de uma determinada data. Os dados do relatório serão os mesmos, mas a consulta mudará bastante. Dessa forma, o melhor é passar a consulta dinamicamente, pois ela seria mais ou menos assim em cada um dos casos:
Select nome, data_nascimento, endereco from estudante where estado = ‘CE’; — quero listar os estudantes do ceará
Select nome, data_nascimento, endereco from estudante where data_nascimento >= ’1980-01-01′; — quero listar os estudantes que nasceram a partir de 1980
Assim vou ter o mesmo arquivo jrxml para gerar os dois tipos de relatórios e o ponto de exclamação serve exatamente para que eu passe a consulta, seja ela qualquer uma das duas acima, como um parâmetro.
sim, eu entendo o de passar a consulta, só queria entender a técnica do ireport, o que a exclamação faz com o parâmetro para o programa entender como uma consulta sql por exemplo, já que passando como uma String normal, não funciona. Obrigado pela atenção.
Nesse caso realmente eu não sei. Acredito que seja algo que eles convencionaram.
Cara, sensacional a sua dica… Tinha lido varios tutoriais, mas nenhum deles era claro como este.
Muito obrigado e meus parabens!
Abraco!
Olá Pablo… muito bacana esse exemplo!
Existem outras formas bacanas de fazer isso ai que você fez.
Quanto a mudar a consulta, já que os campos retornados são os mesmos (e muda apenas o tamanho do ArrayList do resultado), não seria mais fácil “enviar para o Jasper” apenas o “ArryList” com o resultado?
No caso dos campos “serem os mesmos” e os filtros “serem dinamicos”, uma solução elegante seria usar o Jasper apenas para renderizar o relatório. Os dados da consulta poderiam vir filtrados dinamicamente através de um DAO/Service do Hibernate ou iBatis (usando um pool de conexões oferecido pelo proprio servidor de aplicações como no JBOSS ou WebSphere)…
As vantagens de se fazer dessa forma são várias.
Obviamente que cada caso é um caso, um universo diferente.
No exemplo que citei (aplicado em um caso real em um aplicativo corporativo de uso crítico que consultava uma base de dados cabulosa…), ao separar dos relatórios a renderização (neste caso o Jasper não realizava a consulta) dos serviços que faziam as consultas e retornavam “apenas os dados”, tivemos um reuso muito bacana e um ganho significativo em produtividade.
Bacana Thomas, manda um passo a passo de como se faz, pode ser ?
Bom dia. Como faço pra forçar uma quebra de linha? Vi que você simplesmente passou uma String com “\n” mas isso não funcionou comigo, o jasper imprime o caracter de escape literalmente …. “blablabla\nblablabla”
Obrigado pela atenção.
Gustavo, o campo é Static Text ou TextField? Se for Static Text você tem que colocar a quebra de linha normalmente mesmo, no valor do campo. Já a solução que eu mostro, com o “\n” funciona apenas em campos TextField.
Olá Paulo. Eu descobri o que estava fazendo errado. Estava fazendo os testes usando um textfield e um parametro no próprio ireport, sem ligaçao nenhuma com minha aplicaçao… Na hora de compilar o formulario, o ireport pedia o parametro e eu digitava, usando \n mas era impresso literalmente. Coloquei o meu texto com o \n na propriedade default do parametro e aê funcionou… acho que se eu for passar o parametro pela minha aplicaçao tbm vai funcionar…
Obrigado pela atenção
Otimo post Pablo, agora uma duvida como você trabalhou (abriu e fechou) a conexão com o banco de dados do relatorio?
Wanderlei,
Minhas gerações se dão através do código java. Nesse caso, abro uma conexão, passo para o relatório e fecho-a no bloco finally. Algo mais os menos assim:
Connection conn = null;
JasperPrint print;
try {
conn = getConnection();
print = JasperFillManager.fillReport(reportStream, parametros, conn);
JasperExportManager.exportReportToPdf(print, “C:\aux.pdf”);
} catch (Exception e) {
e.printStackTrace();
throw new RelatorioException();
} finally {
try {
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
}
}
Abraço,
Pablo.
Então mas no caso de você passar a conexao e a sql como parametros, como você fecha a conexão depois?
No seu outro tutorial(Tutorial: Técnicas de Geração de Relatórios com JasperReports / iReport) uqe por sinal é muito bom tambem eu vi como abrir mas não tem como fechar?
Mas é o mesmo caso. Você está passando o objeto conexão e a consulta vai como parâmetro. Entretanto, a conexão – assim como a consulta sql – são usadas apenas no momento da geração, depois elas não são mais necessárias. Aí basta você fechar no finally.
Olá….Muito bom o post, só uma dúvida, eu consigo passar duas consultas dinamicamente para o jasper ?
Sim. Basta passar como outro parâmetro. Mas porque você quer fazer isso? Está usando um subrelatório?
Na verdade não gostaria de utilizar subrelatório, e gostaria de saber se teria como passar duas consultas via parametro (uma para cabeçalho e outra para detalhes dos produtos), mas enfim, acabei utilizando subrelatório mesmo…..até q ficou legal….
Muito obrigado pela resposta e ótimo seu post tbm.