quinta-feira, 22 de janeiro de 2009

Guia : JasperReports + Ireport + XML em 5 etapas

O tema do meu primeiro post é bem oportuno para mim, pois além de ser um tema de extrema importância no dia a dia de um DEVILoper, nas ultimas semanas não tenho respirado, pensado, dormido, comido , bebido e ouvido outra coisa que não fosse “cuspir relatórios” (chefe, me desculpe por essa heheh), jrxml's, XML, jasper, iReport, massive compiler, XPath, field description, detail ... enfim, todo esse universo maravilhoso e confuso dos relatórios corporativos utilizando JasperReports , IReport e XML como fonte de dados (isso mesmo Xis EME ÉLE). Bem, mas tirando o meu desabafo de lado vamos ao nosso objetivo: Criar um relatório jasper utilizando um arquivo XML como fonte de dados, apresentando dados simples e iterando uma lista.

Para que minha explicação ficasse o mais simples e objetiva possível, decidi criar este guia em 5 etapas :

Primeira etapa, conhecendo o seu XML de dados

Segunda etapa, criar um novo relatório e definir o XML como fonte de dados no Ireport

Terceira etapa, criar os campos de acordo com seu XML

Quarta etapa, configurando a query XPath do relatório e as descrições de cada campo

Quinta etapa, criando a classe java para emitir o relatório

Obs: Quando eu digo simples, é simples mesmo, não abordarei muitos detalhes sobre Xpath, XML, layout, desenho de relatórios no IReport, classes do jasper... então quem não tiver um mínimo de familiaridade com a ferramenta, ou nunca tiver desenhado um relatório na vida pode ter serias dificuldades ao seguir este guia ò.Ô

Primeira etapa, conhecendo o seu XML de dados

Antes mesmo de desenhar um relatório, temos de saber o que vamos apresentar conhecendo a estrutura do nosso datasource. Neste exemplo iremos apresentar um boletim escolar, com os dados de um aluno, e uma lista com seu histórico de desempenho do semestre. Então vamos ao nosso XML de dados:



 


<?xml version="1.0" encoding="ISO-8859-1"?>

João Pimenta
000123
C



Português
7,0
8,5
7,75
APROVADO


Matemática
6,5
7,7
7,1
APROVADO


Física
4,0
5,5
4,75
REPROVADO


Quimica
6,0
7,5
6,75
APROVADO


História
8,0
9,0
8,5
APROVADO


Ed. Fisica
10,0
8,5
9,25
APROVADO



Segunda etapa, criar um novo relatório e definir o XML como fonte de dados no Ireport:


Para criar um novo relatório clique na barra de menu em Arquivo>Novo Documento ou pressione CTRL + N, aparecera uma janela onde você devera informar as propriedades do relatório, nomeie o arquivo e na aba “i18n” defina a codificação XML para ISO-8859-1 (mesma codificação do XML datasource) para que o relatório tenha suporte a acentos assim como mostra a figura 1.


Figura 1- Criando um novo relatório


Desenhe o seu relatório de acordo com o nosso exemplo mostrado na figura 2, lembrando que os campos do XML que pertencem a uma lista a ser iterada, devem sempre ficar na banda “Detail” do relatório, e que para dados de campos devemos utilizar o elemento “TextField” (Campo Texto) .

Figura 2 – Nosso template do Boletim


Agora vamos definir o arquivo “Boletim.xml” como o datasource do nosso relatório, para isso clique na barra de menu em “Data>Conexões/Fonte de dados “ , na janela que mostra as suas conexões clique em “novo” e na lista de opções clique em “Fonte de dados de arquivo XML” , em seguida defina um nome para a conexão, localize o arquivo XML de dados através do botão “Navegar” e marque a opção “Use a expressão de relatório Xpath quando preencher o relatório” como mostrado na figura 3.

Figura 3 – criando a conexão com o XML datasource

Clique em “Teste” e se houver sucesso clique em “Salvar”.

Terceira etapa, criar os campos do relatório de acordo com seu XML

Para que sejam apresentados os valores do XML de dados em nosso relatório, precisamos definir estes campos, dando a cada um deles um nome e informando através da descrição do campo o caminho da informação. O IReport utiliza o XPath para varrer os valores de um arquivo XML. Para criar os campos, clique em “visualizar>campos do relatório” , aparecera uma janela vazia, pois ainda não definimos os nomes de campos e suas descrições (caminho), nesta janela clique em “novo” para criar um campo, no exemplo a seguir mapearemos o campo ALUNO_NOME do XML, com o tipo “String”, o processo para a criação dos demais campos é praticamente o mesmo, porem existem algumas peculiaridades ao informar a “descrição do campo” (Field Description) que veremos a seguir.

Figura 4 – Mapeando um campo no IReport

Observe que na descrição do campo colocamos a seguinte XPath “//ALUNO_NOME”

, o “//NOME_DO_NO” é utilizado na notação do XPath para buscar o valor de um nó em todo o documento XML, só deve ser utilizado caso o nome deste campo não se repita pelo arquivo e não faça parte de uma lista. Para os campos que tem de ser iterados pela band “Detail”, ou seja, que fazem parte de uma lista, a descrição do campo ficara um pouco diferente, pois utilizaremos a query XPath do relatório, é o que veremos no passo a seguir .

Obs: Neste guia não abordei com profundidade o XPath (XML Path Language) que nada mais é do que uma linguagem de consulta em arquivos XML que também é adotada pelo IReport. Para saber mais consulte os links:

http://www.zvon.org/xxl/XPathTutorial/General/examples.html

http://www.w3schools.com/XPath/default.asp

http://ireport.sourceforge.net/cap7.html

Quarta etapa, configurando a query XPath do relatório e as descrições de cada campo

Vamos escrever a consulta geral do relatório que será utilizada pela band “detail” trazendo as informações de uma lista. Nossa lista consiste em um conjunto de disciplinas que estão no caminho “/BOLETIM/DISCIPLINAS/DISCIPLINA” dentro do XML de dados. Para isto clique em “editar>query do relatório” , aparecerá a janela a seguir:

Figura 5 – A consulta XPath do relatório


Agora crie os campos que ficarão dentro do detail como mostra a figura a seguir:

Figura 6 – Campo dentro do detail

Neste exemplo coloquei o nome do campo totalmente diferente da sua descrição propositalmente apenas para mostra que o nome do campo não precisa necessariamente ser igual ao nome do nó do XML de dados ao qual ele se refere, porem em sua descrição devemos utilizar EXATAMENTE o mesmo nome na consulta XPath. Você deve estar se perguntando agora “onde esta o // antes da palavra ‘NOME’ ?” , e eu lhe respondo: meu caro padawan, na query geral do relatório já informamos o caminho deste campo, por isto apenas o nome do nó do XML de dados se faz necessário . Após criar todos os campos, adicione os mesmos dentro de cada textfield , substituindo a expressão “$F{Field}” por “$F{NOME_DO_CAMPO_CORRESPONDENTE}” (Ex: $F{ALUNO_NOME} ) e teste dentro do Ireport clicando no botão (Executar Relatório usando conexão ativa).

O resultado esperado apresentado no “JasperViewer” (aplicação interna do IReport), será o da figura a seguir:

Figura 7 – Relatório emitido dentro do IReport

Quinta etapa, criando a classe java para emitir o relatório

Agora vamos renderizar nosso relatório através de uma classe java. Para isto crie um projeto java em sua IDE favorita (no meu caso eclipse hehe) , para este exemplo precisaremos das bibliotecas apresentadas na figura a seguir configuradas no classpath de sua aplicação:

itext-1.3.1.jar

jasperreports-3.0.0.jar

xalan.jar

commons-beanutils-1.7.jar

commons-logging-1.0.2.jar

commons-collections-2.1.jar

Figura 8 – Bibliotecas necessárias para o projeto encontradas no diretório “lib” da instalação do IReport

Agora, adicione a seguinte classe ao seu projeto (fique atento ao nome do pacote caso não crie um pacote “com.blogspot.javadevilopers” como no nosso exemplo):


 

package com.blogspot.javadevilopers;

import java.util.HashMap;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRXmlDataSource;
import net.sf.jasperreports.view.JasperViewer;

public class EmiteRelatorio {

public static void main(String[] args) {

try {
//Caminho do arquivo Boletim.jasper (bytecode gerado/compilado do nosso relatorio Boletim.jrxml)
String relatorio = System.getProperty("user.dir") + "/src/com/blogspot/javadevilopers/relatorio/Boletim.jasper";

//Configurando a classe JRXmlDataSource que apontara o caminho do nosso XML de dados e sua pesquisa XPath geral
JRXmlDataSource xml =
new JRXmlDataSource("C:/JavaDEVILopers/Guia_JasperReports_Ireport_ XML_5_etapas_JavaDEVILopers/XMLBoletim.xml",
"/BOLETIM/DISCIPLINAS/DISCIPLINA");

/*Gerando o relatorio (Filling) informando o caminho do relatorio, os parametros
(neste caso nenhum paramentro esta sendo passado ao relatorio, por isso o HashMap esta vazio)
e o objeto JRXmlDataSource configurado)*/
JasperPrint jp = JasperFillManager.fillReport(relatorio, new HashMap(),xml);

//Utilizando o JasperView, uma classe desktop do jasper para visualização dos relatorios
JasperViewer.viewReport(jp,false);

} catch (JRException e) {

e.printStackTrace();
}
}
}


Fique atento aos caminhos de localização do seu arquivo de datasource (XML de dados) e do seu arquivo .jasper (relatorio compilado) e altere na classe, infelizmente eu não estou nem um pouco a fim de entregar de bandeja toda a confighuração do projeto, alias vocês já são bem grandinhos e meu objetivo não é mastigar tudo não, quem tem filho barbado é gato e eu já deixei este código bem comentado , parem de chorar . Execute a sua classe e “tcharam” , temos um relatório renderizado no JasperViewer como mostrado a seguir:


Figura 9 - Relatorio Emitido por nossa classe java


Por fim espero que tenham gostado do meu singelo post, desculpem se não me aprofundei nos detalhes mas brevemente nossa equipe trará novos artigos, tutoriais, dicas e guias com foco também para iniciantes. Qualquer duvida postem nos comentarios deixando seu email para contato ou me contatem pelo email edu.medeirospereira@gmail.com .


Abraços!

14 comentários:

  1. Mto bom seu post! Era justamente o que eu estava precisando. Pelo iReport consegui compilar perfeitamente, mas qndo tento pela minha aplicação os campos do detalhe do relatório não são exibidos. Você teria idéia de pq isso acontece?

    ResponderExcluir
  2. ou o Xpath para varredura do XML esta errado ou o atributo "id" no XML não existe para cada registro do detail no seu datasource... tirando estas duas hipoteses não consigo ver outra causa para tal erro

    ResponderExcluir
  3. As fontes utilizadas pelo relatório podem não estar no PATH "reconhecido" pela aplicação. Isto também é um problema comum em se tratando do Jasper. Muitas vezes o iReport traz consigo as fontes e ao testar com ele o relatório é exibido corretamente, mas ao executar pela aplicação as informações não são exibidas, trazendo consigo páginas "vazias", mas neste caso apenas renderizada incorretamente.

    ResponderExcluir
  4. Eduardo, não creio que seja esse o problema pois estou utilizando esse seu exemplo para testar.

    Charly, também acredito que seja algo errado com a renderização, pois o cabeçalho do relatório é exibido corretamente, apenas o detalhe não aparece.

    ResponderExcluir
  5. Como você está lendo o XML de exemplo? Ou seja, qual o path do arquivo é informado?

    ResponderExcluir
  6. Kelson o código é esse:

    public static void main(String[] args) throws Exception {
    try{
    String relatorio = "C:/Teste/report2.jasper";

    JRXmlDataSource xml =
    new JRXmlDataSource("C:/Teste/boletim.xml","/boletim/disciplinas/disciplina");

    JasperPrint jp = JasperFillManager.fillReport(relatorio, new HashMap(),xml);
    JasperViewer.viewReport(jp,false);

    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    ResponderExcluir
  7. Tentou assim?

    JRXmlDataSource xml =
    new JRXmlDataSource("C:/Teste/boletim.xml","/BOLETIM/DISCIPLINAS/DISCIPLINA");

    Ou seja, tudo maiúsculo!?!?!?

    ResponderExcluir
  8. Tentei, nesse caso dá mensagem de que o documento não possui página.

    ResponderExcluir
  9. Bem, se alterando o DS você obtém uma mensagem que não possui páginas, posso supor que ao gerar o relatório você obtém as páginas, mas com os registros "vazios", certo?
    Se for, muito provavelmente é problema com renderização das fontes mesmo, e neste caso, altere as fontes via iReports para uma outra fonte, bem comum, "Sans" por exemplo, e teste a aplicação novamente.
    Ainda, qual a versão de SO, iReports e SDK você está utilizando?

    ResponderExcluir
  10. Charly, to usando exatamente a fonte "Sans". Quanto a configuração: SO= XP, iReport= 3.6.0 e SDK= 1.6

    ResponderExcluir
  11. Na versão 3.6.1 do JR foi adicionada a exceção JRFontNotFoundException que não existia anteriormente, e como os autores colocaram, muitos problemas de renderização eram provocados por a fonte utilizada não estar disponível pra JVM ativa. Minha sugestão, para que possa corrigir, ou descartar a hipótese do problema de fonte, altere a fonte para a fonte utilizada nos cabeçalhos... Eu já tive problemas onde eu utilizava a mesma fonte mas parte era renderizada e outra não... eu apenas alterei a fonte... o Jasper tem dessas "idiossincrasias"... rsss

    Eu iria comentar que outro problema que poderia ocorrer é você está utilizando a classe Integer para os atributos mas estar vindo valores não inteiros, mas como você falou que no iReports a informação é exibida, não cabe.

    ResponderExcluir
  12. Charly, nas propriedades dos fields existe os campos "Font name" e "Pdf font name" todos estão com os valores SansSerif e Courier respectivamente, alterei os valores pra diversas fontes e mesmo assim o problema continua.
    Testei colocar um dos fields do cabeçalho no detalhe e ele também não foi exibido.

    ResponderExcluir
  13. Pessoal, descobri o problema. O iReport 3.6.0 usa jasperreports-3.5.2 e meu projeto estava com o jasperreports-3.0.0.
    Foi só substituir e adicionar o commons-digester-1.7 pra tudo funcionar. Obrigado ae pela ajuda.

    ResponderExcluir
  14. Muito obrigado pelas dicas, para mim estar muito bom.
    Estava procurando ha muito tempo, finalmente consegui preparar meu relatorio.

    ResponderExcluir