terça-feira, 19 de junho de 2007

Cuidado com SubReports aninhados

Com JasperReport é bom prestar atenção no uso de SubReports aninhados. O problema é com os parâmetros, pois o parâmetro de um relatório não persiste em seus SubReports. Parece pouca coisa, mas tive uma péssima experiência com isso.

Um projeto em que estou trabalhando possui um relatório com um SubReport, e este SubReport possui inclui um outro SubReport. Para achar os arquivos jaspers dos SubReports utilizamos o default de concatenação do parâmetro ${SUB_DIRECTORY}. Acontece que ao definirmos na aplicação este parâmetro, o SubReport não recebe esta definição, de forma que ele acaba utilizando o default na inclusão de seu SubReport. Resultado: Funciona no ambiente de desenvolvimento, mas no ambiente de distribuição o bug acontece.

Para resolver isso é preciso repassar o valor do parâmetro ${SUB_DIRECTORY} para os demais SubReports. Para fazer isso no IReport aperte com botão direito no local de inclusão do SubReport e aperte em properties. Na aba em que se configura o path do SubReport, logo abaixo deste campo existe uma caixa onde se pode definir os valores que devem ser repassados por parâmetro.

quarta-feira, 13 de junho de 2007

Classes de auxílio no uso de JasperReport e IReport

Popular um relatório jasper com uma coleção de beans é simples, o incoveniente é quando se deseja popular com uma coleção de beans que possuem instancias de outros beans como atributo. Para resolver essa sitação, criei dois DataSources do Jasper implementando algo parecido com a EL (Expression language) de forma a navegar seus atributos. São as classes ObjetoDataSource e ColecaoDataSource.

Veja como essas classes funcionam:
- Como funciona a classe ObjetoDataSource para JasperReport
- Como funciona a classe ColecaoDataSource para JasperReport

Se você gostou, pode baixar o projeto timotta-api clicando aqui.

Classe ObjetoDataSource para JasperReport

Com a classe ObjetoDataSource é possível acessar os atributos de qualquer objeto. Basta colocar os campos separados por ponto. Essa classe faz parte do pacote de auxílio no uso de JasperReport e IReport. Mostro abaixo um exemplo simples. Primeiro vejamos as classes de dominio.
public class Pessoa
{
 String nome;
 Date nascimento;
 Endereco endereco;
 List<String> telefones;
 // ... Gets e Sets omitidos ...
}
public class Endereco
{
 String rua;
 short numero;
 // ... Gets e Sets omitidos ...
}

Agora digamos que a gente possui uma intância da classe Pessoa e desejamos imprimir todos seus campos em um relatório Jasper. Para fazer isso basta instanciar um novo ObjetoDataSource, como mostrado abaixo:
Pessoa eu = loadPessoa();
ObjetoDataSource dataSource = new ObjetoDataSource(eu);
JasperReport compilado = JasperCompileManager.compileReport("/tmp/Relatorio.xml");
JasperPrint preenchido = JasperFillManager.fillReport(compilado, new HashMap(), dataSource);
JasperViewer.viewReport(preenchido);

No seu relatorio, para acessar os campos da Pessoa, basta declarar os campos da seguinte forma:
<field name="nome" class="java.lang.String" />
<field name="nascimento" class="java.util.Date" /> 
<field name="telefones" class="java.lang.Object" />
<field name="endereco.rua" class="java.lang.String" />
<field name="endereco.numero" class="java.lang.Short" />

Repare que o campo telefones é declarado como Object, porque a resposta dele não é um List, e sim um ColecaoDataSource. Isso é feito para que se você possa iterar nele utilizando um SubReport.

Neste exemplo, o relatório possuiria apenas uma pessoa, no caso de termos que exibir várias pessoas utiliza-se o ColecaoDataSource. Ou poderiamos ter uma classe, por exemplo, Equipe que possui um atributo do tipo List. Com essas classes as possibilidades são infinitas.

Classe ColecaoDataSource para JasperReport

Com a classe ColecaoDataSource é possível iterar e acessar os atributos dos objetos presentes em uma coleção. Funciona da mesma forma que a ObjetoDataSource, com a diferença de que por ser uma coleção, permite iterar entre uma coleção de objetos. Essa classe também faz parte do pacote de auxílio no uso de JasperReport e IReport. Mostro abaixo um exemplo simples. Primeiro vejamos as classes de dominio:
public class Pessoa
{
 String nome;
 Date nascimento;
 Endereco endereco;
 List<String> telefones;
 // ... Gets e Sets omitidos ...
}
public class Endereco
{
 String rua;
 short numero;
 // ... Gets e Sets omitidos ...
}

Agora vou preencher o relatorio com uma lista de pessoas:
List pessoas = loadPessoas();
ColecaoDataSource dataSource = new ColecaoDataSource(eu);
JasperReport compilado = JasperCompileManager.compileReport("/tmp/Relatorio.xml");
JasperPrint preenchido = JasperFillManager.fillReport(compilado, new HashMap(), dataSource);
JasperViewer.viewReport(preenchido);

No seu relatorio, para acessar os campos da Pessoa, basta declarar os campos da mesma forma como foi feita com a classe ObjetoDataSource. Viu como é fácil?

Agora digamos que ao invés de List, queremos utilizar um Map. É a mesma coisa, só a declaração dos campos no relatório fica um pouco diferente. Isso porque passamos a iterar não por objetos Pessoa, mas por objetos Entry. Veja abaixo:
Map<Integer,Pessoa> pessoas = loadPessoas();
ColecaoDataSource dataSource = new ColecaoDataSource(eu);
JasperReport compilado = JasperCompileManager.compileReport("/tmp/Relatorio.xml");
JasperPrint preenchido = JasperFillManager.fillReport(compilado, new HashMap(), dataSource);
JasperViewer.viewReport(preenchido);

No relatório então, os campos ficariam assim:
No seu relatorio, para acessar os campos da Pessoa, basta declarar os campos da seguinte forma:
<field name="key" class="java.lang.Integer" />
<field name="value.nome" class="java.lang.String" />
<field name="value.nascimento" class="java.util.Date" /> 
<field name="value.telefones" class="java.lang.Object" />
<field name="value.endereco.rua" class="java.lang.String" />
<field name="value.endereco.numero" class="java.lang.Short" />


Simples não é?

Download da timotta-api

Faça o download dos fontes, testes, exemplos e binários:

Versão 0.0.2: http://www.divshare.com/download/5301025-124
Versão 0.0.1: http://www.divshare.com/download/936101-637

Veja as funcionalidades disponíveis na timotta-api 0.0.2: