Relatórios em Java Com JasperReports e Ireport
Relatórios em Java Com JasperReports e Ireport
Downloads
Você pode baixar a versão utilizada do iReport nesse link. Também vamos usar um
banco de dados no treinamento. Para facilitar a importação dos dados preparamos um
arquivo SQL disponível nesse link: financas.sql
Case queira usar a versão mais atual do iReport segue o link da Jaspersoft.
A forma mais comum é o desenvolvedor gerar uma página HTML, por exemplo, com uma
tabela, formatando-a através de CSS. O problema é que isso nem sempre tem a qualidade
e precisão desejada após a impressão. Como, por exemplo, paginar este relatório?
Outra questão: o que fazer se a aplicação não é uma aplicação web? E se ainda
desejarmos numa aplicação web outras saídas, não só HTML com CSS, como também
PDF? Muitas vezes misturamos dados do relatório com sua visualização, sendo
necessário rescrever parte do código para outras saídas.
Isso também significa que estamos dependentes do desenvolvedor para criar um relatório.
Queremos uma abordagem mais simples, uma abordagem que permita criar relatórios
sofisticados sem termos que conhecer detalhes técnicos a fundo.
Uma ferramenta famosa do mundo Java é o iReport. Ele nos fornece uma interface gráfica
bem elaborada para definirmos o relatório e para exportá-lo em vários formatos como PDF,
PPT, OpenDocument, etc.
Utilizaremos o ZIP genérico. Ao baixar o ZIP, será pedido um login e senha, basta clicar no
link logo abaixo que pulará esta etapa. Assim, chegamos na página de download.
Agora com o ZIP baixado, vamos extraí-lo. Temos a distribuição genérica do iReport com
os arquivos, plugin para Netbeans e uma pasta bin com os arquivos de inicialização do
iReport. Cada um dos arquivos destina-se a uma plataforma diferente (Windows, Mac e
Linux).
Dentro do iReport definiremos uma conexão com o nosso banco de dados ( jdbc
connection) que se chamará finanças. O driver é mysql e a nossa URL será finanças. O
login será root, sem senha. Após configurar, salvaremos e testaremos a conexão.
Organização do iReport
Estamos vendo a tela do nosso relatório e no centro o Designer.
O iReport criou colunas para nossos campos e logo abaixo de cada um deles uma espécie
de expression language, para indicar que teremos os valores.
No lado esquerdo temos o Report inspector, que mostra a estrutura do nosso relatório e
também as variáveis mais importantes como os parâmetros, os fields e as variáveis. Em
fields aparecem os campos que selecionamos no momento de criação do nosso relatório.
No lado direito temos as propriedades que mudam sempre quando selecionamos um
elemento dentro do relatório.
Várias páginas foram geradas com os dados das movimentações, mas como podemos
perceber, a data mostra um horário que não queremos apresentar. Voltando ao Designer e
selecionando a data, podemos, no lado direito, adicionar um pattern ( dd/MM/yyyy)
manualmente ou utilizar um assistente de data. Vamos formatar a data mostrando-a sem
horário.
Atividade 1 de 7
A funcionalidade de relatórios é um das tarefas mais rotineiras em sistemas
automatizados. Várias estratégias podem ser utilizadas para isso. Conte-nos se já foi
necessário essa tarefa em seus sistemas e como foi sua estratégia para realizá-lo.
Atividade 2 de 7
Por ser mais simples de ser criado, a estratégia de criar relatórios com HTML é bastante
adotada. Embora mais simples possui algumas limitações. Cite algumas dessas
limitações.
Atividade 3 de 7
Uma das alternativas para a criação de relatórios é usar uma Report Engine. No mundo
java existe duas ferramentas bastante difundidas: IReport e JasperReports. Para que
servem as duas ferramentas?
Atividade 4 de 7
Onde ficam salvas as definições do iReport?
17/12/2013 08:07
Atividade 5 de 7
Baixe o iReport aqui e escolha a versão ZIP (ou a extensão do seu sistema operacional).
Depois ter baixado, extraia o ZIP. Para rodar o iReport é preciso ter Java instalado.
Depois de baixar e executar o iReport segue o Wizard na página inicial (Welcome Window)
para configurar a conexão. Define um conexão JDBC usando o banco MySQL (baseado
no database financas configurado no exercício). Chame a conexão financas. Escolhe o
Driver MySQL (com.mysql.jdbc.Driver) e o nome e senha do usuário. Cuidado para não
esquecer altera a URL do banco (por exemplo jdbc:mysql://localhost/financas).
No próximo passo escolhe o template, por exemplo Simple Blue e define o nome do
relatório.
Depois escrevemos a query. Nesse caso vamos definir um select simples: select * from
movimentacoes.
O campos (fields) são baseados na query. Escolhe todos os fields menos categoria_id e
conta_id. Assim pode finalizar o relatório e testar o preview
Para cada novo relatório é necessário 3 passos até verificar o esboço de um relatório.
Assinale a alternativa que descreve a ordem dos passos.
17/12/2013 08:08
definir relatório com template e query -> verifcar preview -> configurar datasource
configurar datasource -> definir relatório com template e query -> verificar preview
configurar datasource -> verificar preview -> definir relatório com template e query
verificar preview -> definir relatório com template e query -> configurar dasource
definir relatório com template e query -> configurar datasource ->verificar preview
Atividade 6 de 7
Para que se possa criar um relatório é necessário termos uma fonte de dados a serem
manipulados. Para que não seja necessário se preocupar com mais esse problema, vamos
usar dados já prontos.
Pegue o financas.sql e após isso crie um banco no MySQL com nome de financas.
Através do terminal importe seus dados com esse comando: mysql -u root -p -h
localhost --database=financas < financas.sql
Feito isso quantas tabelas foram criadas? E quais são os campos da tabela
movimentacoes?
Atividade 7 de 7
Qual a extensão do nosso recém-criado relatório financas?
17/12/2013 08:08
jar
java
hmtl
jrxml
xml
Explicação
Além disso precisamos ordenar pelo titular e pela data da movimentação. Será
necessário filtrar a query, porque apenas saídas devem ser consideradas, isto é, estamos
interessados apenas nos gastos das movimentações. Simplificaremos o nome de alguns
campos e como resultado teremos: id, data, descricao, tipo, valor e categoria.
Com a query definida, o próximo passo é indicar os fields que utilizaremos, neste caso,
todos. Em seguida, agruparemos os dados pelo titular da conta. Após a finalização,
teremos como resultado a definição do relatório.
Seções e componentes de um
relatório
No lado esquerdo do report inspector, podemos ver as variáveis mais importantes da
estrutura do relatório. Por exemplo, temos os campos que definimos em nossa query.
Baseados nesses campos, criaremos o nosso relatório. O próximo passo agora é
escolhermos uma seção do nosso relatório para arrastarmos estes campos.
Podemos ver uma seção chamada detail. Este é o lugar onde ficam os dados que o
iReport utilizará, sendo assim, os campos do report inspector serão arrastados para esta
área. O iReport, além de criar automaticamente este campos, também adiciona o
cabeçalho para cada um deles.
Existem mais opções no relatório. Por exemplo, no cabeçalho da página, abrindo a paleta
com os componentes disponíveis, vamos utilizar o texto estático arrastando-o para a seção
page header definindo seu valor com Gastos por Contas. Ao visualizar novamente o
relatório, veremos o texto sendo repetido em cada página, mas queremos que ele seja
impresso apenas uma vez. Para isto, basta arrastá-lo para a seção title acima da seção
na qual ele se encontra. Além disso, ele será colocado em negrito e será impresso apenas
no início do relatório.
Ainda é possível mostrar no page footer, nosso rodapé, o número total de páginas,
inclusive a data atual. Basta visualizar mais uma vez para ver o resultado.
Faremos a mesma coisa para a seção sumary, nosso sumário. Novamente iremos
selecionar a opção soma para mostrar no final do relatório o valor total dos gastos.
Visualizando novamente o relatório vemos o total por página e no final o valor total de
todos os gastos.
Apesar do resultado, queremos saber os gastos por contas e uma conta em nosso
relatório é identificada através de um titular. Por isso agrupamos os dados no início do
relatório e olhando no report inspector temos um group header e um group footer para
titular, falta adicioná-los no relatório. Da mesma maneira que podemos adicionar seções,
podemos apagá-las do relatório. Apagaremos o column footer, porque não faz sentido
para o nosso relatório.
O processo é bem semelhante ao que já fizemos: arrastamos o campo, selecionamos a
função de soma e posicionamos o campo dentro do relatório. Verificando o resultado,
vemos que há o total de todos os gastos do titular João Souza e logo abaixo dele vem as
informações de outro titular.
Temos o nosso por_mes group footer. Nele adicionaremos o valor, realizaremos a soma
e apagaremos o que não estamos utilizando. Finalmente, temos um agrupamento do mês
de janeiro e mais a frente um agrupamento do mês de maio.
Atividade 1 de 5
Para criarmos um novo relatório sem template, escolheremos a seguinte opção
17/12/2013 08:09
Simple Blue
Blank Letter
Flower
Cherry
Atividade 2 de 5
No iReport existe um modo visual para criação de queries durante a criação do relatório.
Nesse modo, podemos simplificar os nomes de alguns campos. Que que propriedade
devemos alterar para isso?
17/12/2013 08:09
GROUP
ROOTQUERY
identifier
alias
Atividade 3 de 5
Para usarmos os campos definidos na query, podemos arrasta-los do report inspector para
que seção do relatório?
17/12/2013 08:09
Column Footer
Page Header
Title
Detail
Atividade 4 de 5
Podemos utilizar diversos componentes em nosso relatório. Para isso, podemos abrir o
menu Janela > Paleta. Se quisermos enumerar as páginas de nosso relatório, o que
devemos fazer?
17/12/2013 08:10
Atividade 5 de 5
O iReport nos permite também saber o valor total da soma de algum campo. Por exemplo,
saber o total de gastos do mês. De que forma podemos utilizar a seção Summary para
saber o total de gastos apresentados no relatório?
Explicação
Melhorar o design do relatório
Olá! Vamos continuar com o nosso relatório Gastos por conta. Ele é funcional, mas ao
visualizá-lo, é possível perceber que está mal formatado. Vamos melhorar o seu design e
aumentar o zoom nos ajudará. Atacaremos inicialmente a primeira seção, o title. Podemos
selecionar o elemento diretamente no Designer ou navegar através do Report inspector.
A primeira coisa que faremos é selecionar um retângulo que servirá como background e
escolher uma cor para ele nas propriedades do componente. Ele não terá um traço e será
adaptado levando em consideração o tamanho do seu parent, a seção no qual ele está
incluído. Para tal, usaremos os Formatting Tools - adapt to parent. Agora só falta colocar o
componente para trás com send to back e centralizar o elemento dentro da seção title.
Nosso próximo passo é deixar todos os cabeçalhos com letra maiúscula. Depois disso
vamos alinhar os nomes do cabeçalho, bastando selecionar todos ao mesmo tempo e
aplicar alinhamento pelo centro. Já podemos visualizar o resultado pelo iReport.
O tamanho do campo não é suficiente, sendo necessário adaptá-lo ao seu parent, como
fizemos com nosso background. Na visualização, vemos a nova informação e tudo
centralizado, mas ainda falta o background na cor azul. Voltando ao Designer, ao
selecionar o componente de texto com o titular, podemos alterar a cor de fundo nas
propriedades dos componentes.
Resultados condicionais
As modificações são aplicadas, mas ao visualizarmos mais uma vez o relatório, vemos que
em alguns casos o campo descrição não se posiciona conforme o esperado quando o
mesmo não existe. É apresentado o valor null, que não faz sentido para o leitor do
relatório.
Para resolvermos este problema, basta selecionar o campo descricao e marcar a opção
Blank When Null. Com a opção marcada e visualizando, vemos que o resultado ainda não
é o esperado. Mais uma vez sobre o campo, marcamos a opção Stretch with overflow.
Esta opção organizará a informação mesmo quando não há espaço disponível, sempre
mostrando todas as informações do campo.
Mais formatações
Falta ainda lidar com os campos ou variáveis que agrupam o valor total do mês e da conta.
Primeiro, alinharemos o resultado e cada grupo à direita e definiremos o pattern currency
para eles. Depois do pattern definido, criaremos outro retângulo na cor azul que ocupará
todo o espaço de seu parent, produzindo um efeito que já fizemos.
No lugar de criarmos mais um retângulo, o copiaremos para o próximo grupo. Por fim,
jogaremos os retângulos para trás com send to back. Visualizando outra vez, vemos o
resultado esperado. Os agrupamentos aparecem na cor azul para se destacarem no
relatório.
Para esta situação, o iReport oferece parâmetros. Os parâmetros são valores passados da
aplicação para o JasperReport, o responsável pela renderização do relatório. Já existem
parâmetros pré-definidos, por exemplo, de localização visível no Report Inspector.
Podemos adicionar nossos parâmetros através do menu, e uma vez feito isso, podemos
alterar as suas propriedades pelo editor. O nome do nosso parâmetro será DATA_INI e seu
tipo java.util.Date. Criaremos outro parâmetro que representa a data final, novamente
no Report Inspector. O nome dele será DATA_FIM, também do tipo Date.
Para não termos que definir toda vez a data, podemos definir um valor padrão. Nos
parâmetros criados, desmarcamos a opção Use as a prompt e editamos Default Value
Expression. Utilizaremos a classe SimpleDateFormat, que receberá um pattern com o
formato da data desejada e que realizará um parse da 01/08/2012.
Veja o código:
new java.util.SimpleDateFormat("dd/MM/yyyy").parse("01/08/2012");
Faremos a mesma coisa para DATA_FIM, desabilitando o prompt e alterando o valor que
sofrerá o parse, neste caso, final de agosto. Renderizando novamente, percebemos que o
prompt não aparece mais e mesmo assim tivemos nosso relatório filtrado.
Atividade 1 de 6
Muitas vezes precisamos formatar um elemento fazendo com que ele ocupe toda a área
do elemento pai no qual esta inserido. Isso é possível através do iReport acessando:
17/12/2013 08:11
Atividade 2 de 6
Há situações em que é necessário formatar o conteúdo de algum campo (field) que
arrastamos para dentro do nosso relatório. Realizamos esta tarefa através do:
17/12/2013 08:13
Report inspector
Formatting Tools
Expression Editor
Atividade 3 de 6
Para cada campo no relatório é possível definir um padrão. Cada padrão permite
configurações específicas, por exemplo, se o padrão for numérico, podemos escolher o
número de casas decimais. Qual é o nome da opção que permite escolher um padrão e
qual o padrão para campos que representam dinheiro?
17/12/2013 08:13
Expression Editor, currency
Atividade 4 de 6
Não faz sentido apresentar no relatório "null" para os campos sem valor. Podemos resolver
facilmente isto clicando no campo, e marcando a propriedade:
17/12/2013 08:14
Send to back
Atividade 5 de 6
É comum abreviarmos o texto de alguns campos para liberarmos espaço para outros,
sendo assim, como imprimir apenas a primeira letra do campo "Tipo" do nosso relatório?
Atividade 6 de 6
Parâmetros são valores passados da aplicação para o JasperReport e existem vários
parâmetros pré-definidos.
Quais são os passos que devemos seguir para criar nossos parâmetros?
Explicação
O iReport já vem com vários charts disponíveis nesta janela. O chart que queremos é o
Stack Chart(uma pilha de cores). Queremos visualizar os gastos agrupados por titular,
portanto, cada pilha representará um titular de uma conta, e cada cor fará referência à
soma de um tipo de gasto.
Na próxima página podemos selecionar qual fonte ou dataSet o chart utilizará. O iReport
oferece o dataSet do relatório principal, o main report dataset. O problema é que a query
do dataSet não agrupa os gastos por titular, sendo necessário alterá-la.
Após confirmarmos, podemos seguir para o próximo passo e adicionar todos os campos
no dataSet. Não haverá nenhum agrupamento. Finalizando a definição do dataSet,
aparece o novo elemento no report inspector.
Criação do gráfico
Com o dataSet definido, voltaremos para criação do gráfico. Para simplificar, ajustaremos
primeiro o tamanho da seção. Novamente arrastaremos o elemento chart para a seção
sumary, e na próxima tela escolheremos o Stack Chart. O dataSet será o chartDataSet.
O primeiro passo é definir a série, o valor da cada cor dentro de uma pilha. Cada cor é
uma categoria de gastos. Consequentemente escolheremos o campo categoria. Na tela
seguinte, a legenda estará definida abaixo do eixo X. Cada pilha é relacionada com um
titular, e os valores das categorias são representados pela soma dos gastos. Podemos
confirmar o gráfico e ajustá-lo na seção sumary, ocupando todo o espaço.
Nesse caso, usaremos o dataSet do relatório principal. Na próxima tela definiremos o que
queremos mostrar em cada linha. Neste caso, o nome do titular. Depois, definiremos o que
usar no cabeçalho da coluna, que será o campo data(apenas o mes). O próximo passo é
decidir qual valor aparecerá em cada célula.
Queremos a soma dos gastos por mês deste titular, então, escolheremos o campo valor
com a função sum.
Na última tela podemos mudar o layout padrão da tabela. Usaremos a cor bege. Depois de
confirmado, o iReport criará automaticamente uma nova aba para mostrar o design da
tabela. Nesta tabela podemos ver os dados dos cabeçalhos e células. Além disso, também
foram criados campos para somar cada linha e coluna.
No relatório principal, precisamos ajustar o layout que está em conflito com o gráfico,
diminuindo o espaço da tabela. Depois disso podemos visualizar o relatório. Indo para a
última página do relatório, aparece a tabela, com os titulares em cada linha e a soma do
mês na coluna. Porém, a tabela foi criada baseada em apenas um mês porque nossa
query principal a utilizou num período muito pequeno.
A tarefa agora é aumentar este período, mas antes vamos inserir quebras de páginas
entre o gráfico e a tabela para melhorar o design. Já existe um componente pronto na
paleta. Para aumentar o período é preciso alterar o parâmetro DATA_INI. Podemos
acessá-lo pelo report inspector, selecionado a opção para editar a expressão, mostrando
os meses junho, julho e agosto. Novamente, usando o preview e indo para a última página,
percebemos que a tabela foi criada com os três meses.
Usando sub-relatórios
O gráfico e a crosstable são opções para visualizar as informações, diferente do relatório
principal. Existem casos aos quais queremos integrar dados complexos, nesse caso, é
preciso um novo relatório em anexo ao relatório principal. Com o iReport, podemos criar e
integrar um novo relatório através do elemento subreport, que se encontra na paleta.
Arrastando este elemento na seção title, um wizard de criação de relatórios aparecerá
automaticamente.
O iReport abre uma nova aba para o design do relatório. O sub-relatório é um relatório
comum, então existem as mesmas seções e variáveis disponíveis. Também
encontraremos parâmetros, campos e variáveis no report inspector.
Integração do sub-relatório e
relatório mestre
Vamos voltar ao relatório mestre. O nosso sub-relatório deve ficar no início, na seção title.
É preciso ajustá-lo para o sub-relatório ter espaço suficiente.
Atividade 1 de 6
De onde vêm os dados que serão representados no gráfico?
17/12/2013 08:16
dataSet
dataComponent
data
source
dataSource
Atividade 2 de 6
Qual é o componente que utilizamos caso seja necessário resumir e agrupar os dados de
um relatório?
17/12/2013 08:16
groupData
grouper
crosstable
dataSumary
sumary
Atividade 3 de 6
Quando queremos integrar dados complexos em nossos relatórios utilizamos o seguinte
elemento:
17/12/2013 08:16
dataset
Stack Chart
crosstable
subreport
Atividade 5 de 6
É possível fazer um Gráfico de Linha utilizando o Stack Chart? Por quê?
38
Explicação
Downloads
A versão utilizada do JasperReports Library se encontra nesse link. Também preparamos
um projeto com todas as bibliotecas necessárias que vem preconfigurado para Eclipse.
O projeto pode ser baixado pelo link: cap5-movimentacoes.zip
A versão mais atual do JasperReports Library se encontra aqui. Caso queira baixar o
driver do MySQL (Connector J) separado segue também o link.
Introdução ao Report Engine
JasperReports
Vimos como criar e exportar relatórios usando o designer iReport. Para esta aula
focaremos na utilização do nosso relatório dentro de uma aplicação Java.
Utilizamos o iReport para definir os componentes do relatório. A definição foi salva dentro
de um arquivo XML com a extensão jrxml. Os dados vieram do banco MySQL e no final
tudo foi exportado em PDF para podemos visualizar.
Antes de exportar com o Report Engine é preciso realizar um passo intermediário, que
consiste na compilação do jrxml para um arquivo com a extensão jasper. O iReport
sempre compilava e gerava o arquivo durante as exportações. O relatório compilado será
passado para o Report Engine com os parâmetros da aplicação e a conexão que define a
fonte de dados. Com todos esses informações, poderemos gerar o relatório e exportá-lo,
por exemplo, no formato PDF.
Preparação do projeto
Vamos gerar o relatório programaticamente usando o Eclipse como IDE. Já temos um
projeto preparado chamado gastos. A pasta lib é para as nossas bibliotecas e possui o
driver do MySQL, que já foi definido em nosso classpath. Na pasta src já temos a classe
ConnectionFactory para obter conexões com o banco MySQL
(jdbc:mysql://localhost/financas).
Além disso, temos uma classe de testes que abre uma conexão através da Report Engine.
Vamos chamar uma vez o método main. Executou sem problemas.
O próximo passo é copiar as definições dos relatórios, ou seja os arquivos jrxml. Vamos
copiar os dois arquivos (o relatório principal e o sub-relatório) para a raiz do projeto.
Na pasta apresentada já temos o ZIP baixado, então vamos extrair os arquivos. Entrando
na pasta extraída encontramos o JAR principal na sub-pasta dist, o arquivo
jasperreports-4.7.0.jar. Esse JAR ficará na pasta lib do projeto no Eclipse, vamos
arrastá-lo realizando uma cópia.
Além do JAR principal há mais dependências que estão na pasta lib da distribuição do
JasperReport. Nessa pasta encontramos diversos JARs. Queremos gerar o PDF em nosso
projeto e para isso esses JARs serão necessários: o primeiro é o itext, que o JasperReport
utiliza para gerar PDF. Como nosso relatório também gera um gráfico, utilizaremos o
jfreechart e jcommon. Por último, utilizaremos alguns JARs dos Apache Commons.
Compilação programática
Com as bibliotecas e com o classpath configurado podemos começar a implementar. Para
compilar o arquivo jrxml usaremos a classe JasperCompileManager. Ela possui vários
métodos estáticos para compilar o XML. Nós utilizaremos o método compileTofile que
recebe o nome do XML. É preciso tratar a exceção checked JRException.
Vejo o código:
JasperCompileManager.compileReport("gasto_por_mes.jrxml");
}
}
O problema é que o nosso relatório ainda está configurado para utilizar Groovy como
linguagem padrão. Podemos alterar este padrão para a linguagem Java abrindo o arquivo
jrxml no iReport e selecionando o elemento raiz no Report Inspector.
Com esta alteração, compilaremos o relatório mais uma vez. Agora temos o arquivo
jasper gerado.
Preenchendo o relatório
Falta agora carregar este arquivo e preencher o relatório com os dados com base na
conexão e os parâmetros da aplicação. Para essa tarefa existe uma classe
JasperFillManager que possui o método fillReport que é sobrecarregado. Utilizaremos
a versão que recebe o nome do arquivo jasper, o mapa de parâmetros e a conexão. Só
falta inicializar o mapa de parâmetros. Criaremos um HashMap sem nenhum elemento, por
enquanto:
public class TesteRelatorio {
JasperCompileManager.compileReport("gasto_por_mes.jrxml");
JasperFillManager.fillReport("gasto_por_mes.jasper", parametros,
connexao);
connexao.close();
}
}
Com isso, preencheremos mais uma vez o relatório com o JasperFillManager, mas
primeiro comentaremos a linha que gerou o arquivo jasper, já que não há a necessidade
de recriá-lo.
Além disso, através do mesmo método, definimos o OutputStream para configurar o nome
do arquivo PDF. Nesse exemplo usamos um FileOutputStream como fluxo de saída e o
arquivo se chamará gasto_por_mes.pdf.
JasperCompileManager.compileReport("gasto_por_mes.jrxml");
connexao.close();
}
}
Após a chamada do main e depois de atualizar, aparecerá o PDF gerado na raiz do
projeto. Abrindo o relatório podemos visualizar os dados das contas seguidas pelas
movimentações. Como esperado, as informações encontram-se nas últimas páginas: o
gráfico e o crosstable.
Atividade 1 de 8
Qual é a extensão da definição do relatório (não compilado)?
17/12/2013 08:19
java
xml
jasper
ixml
jrxml
Atividade 2 de 8
Como se chama a classe que compila o arquivo jrxml?
17/12/2013 08:19
ReportCompiler
JrxmlToJasperCompiler
JasperCompiler
JasperCompileManager
CompileJrxml
Atividade 3 de 8
Qual é a extensão do relatório compilado?
17/12/2013 08:19
java
jasper
class
jrxml
bin
Atividade 4 de 8
Qual é a responsabilidade da classe JasperFillManager?
17/12/2013 08:20
Exportar o relatório
Compilar o relatório
Atividade 5 de 8
Queremos exportar o relatório para HTML. Dado o código abaixo, qual implementação
deverá ser escolhida no lugar de XXXXX?
stream.close();
conexao.close();
38
Atividade 6 de 8
Baixe o projeto movimentacoes aqui . Importe-o no Eclipse (File -> Import -> General ->
Existing Project into Workspace -> Select Archive) e verifique se todas as bibliotecas da
pasta lib estão dentro do classpath.
Atividade 7 de 8
Após a compilação do relatório, na raiz do projeto movimentacoes encontra-se o arquivo
financas.jasper.
Atividade 8 de 8
Na classe TesteGeraRelatorio experimente outras saídas como HTML (JRHtmlExporter)
ou XLS (JRXlsExporter).
Explicação
Downloads
Para esta aula é preciso ter instalado Eclipse na versão Java EE. Você pode baixar o
Eclipse no link: http://www.eclipse.org/downloads. Além disso, usaremos o Apache
Tomcat 7 que se encontra no http://tomcat.apache.org. Também temos um projeto
preconfigurado com todas as bibliotecas necessárias disponível: aqui
Caso você tenha dúvidas sobre a instalação do Tomcat verifique a apostila aberta do
treinamento FJ-21.
Foi necessário definir a entrada, o mapa de parâmetros e estabelecer uma conexão com o
banco de dados, tudo como parâmetro do método fillReport(..) da classe
JasperFillManager, que lê e preenche o relatório.
O relatório preenchido é um objeto do tipo JasperPrint, que precisa ser processado por
um JRExporter. Escolhemos JRPdfExporter que transforma o objeto JasperPrint em
PDF.
@WebServlet("/gastos")
public class RelatorioServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
}
}
A nossa servlet está preparada, mas antes de usar o GeradorRelatorio é preciso copiar o
arquivo jasper do projeto anterior. Para isso criaremos uma pasta jasper dentro da pasta
WEB-INF. Nesta pasta colocaremos os dois relatórios compilados, o principal e o sub-
report.
Voltando para a servlet, também copiamos o código que representa a entrada do relatório,
ou seja, nome do relatório, parâmetros e a conexão. Já fecharemos a conexão. A
chamada do método close() exige um try-catch, pois estamos dentro de uma servlet.
Colocaremos todo o código relacionado com a geração do relatório nesse bloco try-
catch. Dentro do bloco catch lançaremos uma ServletException.
Como estamos em um aplicação web, o fluxo de saída é ligado à resposta. Através dela
pegamos o OutputStream, um ServletOutputStream:
response.getOutputStream()
O problema é que o arquivo jasper está dentro da pasta /WEB-INF/jasper. Falta indicar o
caminho até esta pasta. Isso é feito através do ServletRequest que possui o método
getServletContext().getRealPath(..) que recebe o caminho que desejamos acessar.
Passando o caminho a partir da raiz da aplicação web:
/WEB-INF/jasper/gasto_por_mes.jasper .
@WebServlet("/gastos")
public class RelatorioServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
try {
String nome =
request.getServletContext().getRealPath("/WEB-INF/jasper/gasto_por_mes.jasper
");
Connection connection = new ConnectionFactory().getConnection();
Map<String, Object> parametros = new HashMap<String, Object>();
GeradorRelatorio gerador = new GeradorRelatorio(nome, parametros,
connection);
gerador.geraPDFParaOutputStream(response.getOutputStream());
connection.close();
} catch (SQLException e) {
throw new ServletException(e);
}
}
}
Dentro da tag body criaremos o formulário que já preparamos antes. Repare o atributo
action que chama a URL da Servlet através do método POST. Também temos um botão
para submeter o formulário.
<html>
<body>
</body>
</html>
</form>
</body>
</html>
Passando parâmetros da
requisição para o relatório
Em nossa servlet leremos esses parâmetros. Voltando para Eclipse e abrindo a classe
RelatorioServlet usaremos o método getParameter(..) do objeto request para receber
o parâmetro data_ini, a mesma coisa para o parâmetro data_fim, copiando a linha
anterior e renomeando as variáveis.
String dataIni = request.getParameter("data_ini");
String dataFim = request.getParameter("data_fim");
Os parâmetros são ainda do tipo String, porém o nosso relatório espera receber as datas
como java.util.Date. É preciso parsear os dois parâmetros usando a classe
SimpleDateFormat. Essa classe recebe um pattern no construtor que define o formato da
data, em nosso caso dd/MM/yyyy. Com o SimpleDateFormat podemos usar o método
parse(dataString) para criar um objeto Date a partir da String.
Por último é preciso adicionar os dois objetos Date no mapa que foi passado para a classe
GeradorRelatorio. Chamaremos o método put( .. , .. ) do mapa que recebe dois
argumentos, uma chave e um valor. O valor é o objeto data que criamos pelo
SimpleDateFormat e a chave definda na fase de design do relatório. As chaves se
chamam DATA_INI para a data inicial e DATA_FIM para a data final:
@WebServlet("/gastos")
public class RelatorioServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
try {
String nome =
request.getServletContext().getRealPath("/WEB-INF/jasper/gasto_por_mes.jasper
");
Connection connection = new ConnectionFactory().getConnection();
connection.close();
} catch (SQLException e) {
throw new ServletException(e);
} catch (ParseException e) {
throw new ServletException(e);
}
}
}
Atividade 1 de 7
Qual implementação do JRExporter nós utilizamos para transformar um objeto
JasperPrint em PDF?
17/12/2013 08:23
JROdtExporter
JRHtmlExporter
JRPdfExporter
JRXmlExporter
JRTextExporter
Atividade 2 de 7
Como estamos em um aplicação web, o fluxo de saída está ligado à resposta. Como,
através dela, pegamos o OutputStream?
17/12/2013 08:23
response.getWriter()
request.getOutputStream()
response.getOutputStream()
response.getContentType()
Atividade 3 de 7
Como indicamos para a Servlet que o arquivo jasper (gasto_por_mes.jasper) está dentro
da pasta /WEB-INF/jasper?
17/12/2013 08:23
request.getServletContext().getRealPath("/WEB-INF/jasper/gasto_por_mes.jasper")
request.getWriter().getRealPath("/WEB-INF/jasper/gasto_por_mes.jasper")
request.getServletContext("/WEB-INF/jasper/gasto_por_mes.jasper")
request.getContextPath("/WEB-INF/jasper/gasto_por_mes.jasper")
response.getServletContext().getRealPath("/WEB-INF/jasper/
gasto_por_mes.jasper")
Atividade 4 de 7
Porque foi preciso converter os parâmetros (parsing) da aplicação web?
Atividade 5 de 7
No vídeo, o usuário final preenche um formulário para filtrar o relatório pela data inicial e
final. Como foram passados os parâmetros para o relatório jasper?
17/12/2013 08:24
É preciso ter instalado o Tomcat 7 no Eclipse. Você pode baixar a versão do Tomcat aqui.
Basta baixar o ZIP e configurar o server no Eclipse. Caso você tenha dúvidas sobre a
instalação do Tomcat verifique a apostila aberta do treinamento FJ-21 aqui (Capítulo 3).
Explicação
Introdução ao projeto
Para esta aula, utilizaremos o relatório que foi criado na primeira aula, aquele que lista
movimentações. Nesse relatório configuramos a conexão com o banco MySQL
executando uma simples query que selecionava todos os dados da tabela movimentacoes.
JasperCompileManager.compileReportToFile("financas.jrxml");
connexao.close();
}
}
Desacoplando os dados e a
interface JRDataSource
Ao investigar o método fillReport(..), podemos ver que foi passado a conexão.
Justamente nessa linha, amarramos o relatório ao banco de dados. Ao visualizar as
versões do método fillReport(..) no Eclipse, percebemos que ele é sobrecarregado.
Há uma versão que recebe um objeto do tipo JRDataSource no lugar da conexão. Essa
JRDataSource é a abstração da fonte de dados.
Para conhecer melhor esta classe, vamos procurar pelo seu Javadoc na Internet:
http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/JRDataSource.html
Ao seguir o link da API, estamos vendo todas as classes e packages disponíveis.
Procuraremos pelo JRDataSource para chegar em seu javadoc. Na documentação,
podemos ver que JRDataSource é uma interface que declara apenas dois métodos:
getFieldValue(..) e next() .
Vamos declarar a lista acima e carregar as movimentações usando o DAO que já está
preparado no projeto. A classe MovimentacaoDAO possui o método todos() que devolve
todas as movimentações do banco de dados.
Antes de testar, vamos verificar o método todos() do DAO. Podemos ver o SQL e o uso
do PreparedStatement para executar o SQL. Depois, usaremos o resultado da query
(ResultSet) para criar em cada iteração uma nova movimentação. As movimentações
ficam guardadas em uma lista que representa o retorno do método todos().
Geração do relatório e as
primeiras incompatibilidades
Vamos fechar a classe MovimentacaoDAO e executar a classe TestGeraRelatorio para
testar a DataSource. Após a execução, é gerada uma exceção acusando um problema de
cast entre GergorianCalendar e java.sql.Date.
O problema está no arquivo jrxml. O field data foi declarado como java.sql.Date. Essa
definição ocorreu quando definimos o relatório com iReport. Usamos uma query SQL e o
iReport associou automaticamente tipos com campos da query. O mais correto é definir os
tipos, independentemente da fonte de dados. Ou seja, para a data, usando a classe
java.util.Date.
Vamos rodar novamente a classe Teste, só descomentando a linha que compila o jrxml,
pois alteramos o relatório. Novamente foi gerada uma exceção de cast, pois a
incompatibilidade entre os tipos GregorianCalendar e java.util.Date continua.
Após outro teste, recebemos mais uma exceção, agora não mais relacionada com a data,
mas com a enumeração TipoMovimentacao que é incompatível com o tipo
java.lang.String.
Isso gera um acoplamento entre o relatório e o modelo. Queremos que os dois lados
evoluam separadamente. Ou seja, mudanças no relatório devem não influenciar o modelo
e vice-versa. Os ajustes realizados no modelo foram apenas um exemplo e não eram
necessários.
Vamos criar a classe pelo Eclipse definindo o atributo Movimentacao. Vamos gerar também
um construtor que recebe a movimentação. Segue o primeiro esboço da classe:
As mudanças necessárias relacionadas com os tipos serão feitas nesta classe auxiliar e
não no modelo da aplicação. Sendo assim, alteraremos primeiro o retorno do método
getData(), novamente utilizando java.util.Date com o método getTime(). Vamos
mudar o método getTipoMovimentacao(), que retorna uma String utilizando o método
name() da enumeração.
Antes de testarmos novamente, comentaremos a linha que compila o arquivo jrxml. Após
a execução e atualizando o projeto, foi gerado o PDF. Vamos abrí-lo. Todos os dados
aparecem, como esperado.
Resumo da aula
Resumindo, vimos como podemos abstrair a fonte de dados usando a interface
JRDataSource. A implementação concreta que usamos foi a
JRBeanCollectionDataSource. Com ela podemos utilizar qualquer coleção de Java Beans
como fonte de dados. O objetivo era usar a lista de movimentações para o nosso relatório.
Carregamos a lista pelo DAO e passamos ela para a DataSource. Percebemos que era
preciso adaptar o relatório ou a classe Movimentação. Isso gerou um acoplamento que
resolvemos através de uma classe auxiliar MovimentacaoRelatorio, e que também serviu
como um adaptador entre o nosso modelo e o relatório.
Atividade 1 de 7
Como podemos abstrair com JasperReports a fonte de dados da entrada concreta
utilizada?
17/12/2013 08:25
Atividade 2 de 7
Qual é o argumento que a classe JRBeanCollectionDataSource recebe no construtor?
17/12/2013 08:25
java.util.Set
java.util.Map
java.util.Collection
java.sql.Connection
java.util.List
Atividade 3 de 7
Qual implementação da interface JRDataSource podemos utilizar para ler, por exemplo, um
arquivo CSV (ComaSeparatedValues)?
17/12/2013 08:25
JRXmlDataSource
JRBeanCollectionDataSource
JRXlsDataSource
JRCsvDataSource
Atividade 4 de 7
Por que não devemos utilizar o metodo fillReport recebendo a conexão como
parâmetro? Como podemos contornar esse problema?
Atividade 5 de 7
Podemos usar uma implementação concreta para receber qualquer coleção de Java Bean
como fonte de dados. Por exemplo, se quisermos usar uma lista de movimentações em
nosso relatório. Que implementação é essa?
17/12/2013 08:26
JRBeanCollectionDataSource
JRResultSetDataSource
JRAbstractBeanDataSource
JRBeanArrayDataSource
Atividade 6 de 7
Baixe o projeto movimentacoes-csv aqui. Extraia e importe o projeto no Eclipse.
dataSource.setDatePattern(
Atividade 7 de 7
No projeto movimentacoes-csv do exercício anterior se encontra a classe
GeradorRelatorio que recebe no construtor a conexão JDBC.
Refatore a classe GeradorRelatorio para trabalhar com qualquer datasource, não use mais
a conexão e sim a interface JRDataSource.