4 de junho de 2011

JasperReports 4.0.1: Utilizando Subreports e parâmetros

Para esta não ser uma postagem muito extensa, irei utilizar o mesmo projeto e o mesmo relatório criado na postagem: agrupando de dados com JasperReports. Para completar esse projeto, logo abaixo do agrupamento eu vou listar os dados dos cliente, ou seja, terei um relatório de clientes embutido no relatório de pedidos (claro que pode ser feito outras coisas, este é apenas um exemplo)

Começando...
Então lá no projeto Pedidos (link acima), dentro do pacote pedidos.dao, vamos criar uma classe Java como o nome de ClienteDAO, seguindo a mesma linha da classe PedidoDAO, temos:
public class ClienteDAO {

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("PedidosPU");
    EntityManager em = emf.createEntityManager();

    public List<Cliente> listarClientes() {
        List<Cliente> clientes = null;
        try {
            Query query = em.createQuery("Select c from Cliente c order by c.nome");
            clientes = query.getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            em.close();
        }
        return clientes;
    }
}

No iReport, vamos editar o relatório pedidos.jrxml, criando também no link da postagem acima.
Para começar no relatório vamos adicionar a band Summary:


Precisamos criar um parâmetro que será enviado pelo projeto, para isso no Report Inspector, clique com o botão direito em Parameters, e em Adicionar Parameter:


Selecione o parameter1 e vá nas propriedades para configurá-lo, altere o nome para listaClientes e o Parameter class para java.util.List, pois passaremos por parâmetro uma lista de clientes:


Na paleta procure pelo elemento Subreport, arraste-o para a band Summary, a partir de agora siga as imagens abaixo:


Clique em próximo, na janela abaixo selecione o tipo Blank A4:


Clique em próximo novamente, como estamos usando JavaBean datasource, e não utilizamos datasource, configure como na imagem:


Avance de novo:


Mais uma vez:


Na janela abaixo informe o nome do Subreport, nesse caso será chamado de pedidos_subreport:


No passo 7, habilite a opção "Use a JRDatasource expression", e clique no botão do lado da caixa de texto, a seguinte janela aparecerá, nela nós convertemos a listaClientes em um JRBeanCollectionDataSource :


Ao clicar em Apply voltamos para a seguinte janela:



E aqui basta finalizar...
No relatório que foi criado, deixe apenas as bands - Title, Column Header e Detail 1:


Como será uma lista de clientes, precisamos pegar da classe Cliente os seguintes fields - codigo, nome e limiteCredito... para isso em Report Query, na aba JavaBean Datasource:

Feito isso...
Como visto em postagens anteriores, estruture o relatório para que fique assim:


Ah, lembre-se de nas propriedades do relatório alterar a linguagem para Java, já que estamos usando JavaBean datasource:


Depois disto basta compilar nosso subreport, para poder gerar o pedidos_subreport.jasper 

Voltamos ao pedidos.jrxml, e vamos fazer umas ultimas configurações, temos este resultado:

Precisamos criar mais um parâmetro que será uma String com o caminho de onde encontrar o pedidos_subreport.jasper, repita o mesmo passo que foi feito para criar o listaClientes, mas dessa vez use as seguintes propriedades:


Agora precisamos setá-lo no lugar certo... para isso: veja que na band Summary tem um elemento que representa o subreport, clique nele e vá nas propriedades, procure por Subreport Expression, e configure-o assim:


Feito todos esses passos, pode compilar novamente o relatório principal (pedidos)...
Caso não ocorra nenhum erro, voltamos ao projeto Pedidos no NetBeans.

No projeto..
No pacote pedidos,controle.relatorios, substitua o pedidos,jasper pelo que foi compilado agora, e adicione o pedidos_subreport.jasper, ficando assim:

Para finalizar de vez, precisamos fazer algumas alterações no método gerarRelatorio() da classe PedidoControle.java:
public void gerarRelatorio() {
     String arquivo = "src/pedidos/controle/relatorios/pedidos.jasper";

     pedidoDAO = new PedidoDAO();
     clienteDAO = new ClienteDAO();

     Map parametros = new HashMap();
     parametros.put("localizacaoPedidosSubreport", "src/pedidos/controle/relatorios/pedidos_subreport.jasper");
     parametros.put("listaClientes", clienteDAO.listarClientes());

     JRDataSource jrds = new JRBeanCollectionDataSource(pedidoDAO.listarPedidos());
    gerarRelatorioDesktop(jrds, parametros, arquivo);
}

No começo da classe lembre-se de adicionar a declaração do ClienteDAO, assim como foi feito para PedidoDAO...
Vamos ver o que foi alterado no método...
Primeiro, depois de declarado o atributo clienteDAO no inicio da classe, na linha 5 estamos inicializando-o.
Da linha 7 a 9, criamos um Map, com os parâmetros que criamos lá no iReport... e por fim... na linha 12, passávamos o parâmetro como null, agora este é substituído e passamos o Map parametros por parâmetro.

Assim podemos executar nosso projeto e ver o seguinte resultado:


Por aqui ficamos... admito que tive alguns probleminhas ao conseguir utilizar o JavaBean datasource no subreport, o que me levou a pedir um help no Twitter onde o @altitdb me ajudou! por isso deixo os devidos créditos à ele :)

Créditos da postagem: Altieres de Matos, pois ele salvou minha aula sobre Subrepot hehe (pois é, tive problemas ao usar Subreport com JavaBean datasource)

26 comentários:

  1. Andii, primeiramente parabens pelos tutoriais. Segue a risca seu tutorial sobre subreports, mas não consigo gerar o relatorio com o subrelatorio. Primeiro da o seguinte erro: new net.nf.jasperreports.engine.data.JRBeanCollectionDataSource cannot be resolved to a type. Então mudei o nf por sf e o erro sumiu.
    Porem quando tento compilar novamente o erro a seguir é dado:
    Compilation exceptions: com.jaspersoft.ireport.designer.compiler.ErrorsCollector@1f9fb0c net.sf.jasperreports.engine.JRException: An error has accurred compiling the subreport: C:\Users\PimentaAgro\Downloads\iReport-4.0.1\ireport\fonts. Não sei onde está o erro.

    ResponderExcluir
    Respostas
    1. Conseguiu resolver ?? estou com o mesmo problema

      Excluir
    2. clica nas propriedades do subreport vai em subreport Expression e concatena o seu arquivo jasper

      $P{caminhoSubReport1} + "reportPay_subreport1.jasper"

      Excluir
  2. O meu está dando o mesmo erro, Matheus Virtudes.

    Ele diz ainda: "Access Denied" apontado o caminho: c:\..\ireport\fonts.

    Se alguém souber o que pode ser, por favor de um help!

    ResponderExcluir
  3. Olá! Já tive o mesmo problema, mas recompilando ele, resolveu.

    ResponderExcluir
  4. olá....
    sabe o motivo do relatório não encontrar o sub-relatório no .jar da aplicação ?

    obrigado?

    ResponderExcluir
  5. Olá Lucas, também tive problemas desse tipo ao fazer testes mais a fundo com a parte de Desktop, acredito que tenha algum código que dê o caminho exato de dentro do .jar, mas até eu descobrir isso, a solução mais viável foi jogar os relatórios dentro de uma pasta no C:/ por exemplo, claro, essa não é a melhor solução, mas como mecho pouco com a parte Desktop acredito que não poderei te ajudar muito quanto a esse problema :(

    ResponderExcluir
  6. Amigo como faco para passar um array list q eu mesmo estou populando...
    List v = new ArrayList();
    v.add(new vencimentos("20/04/2012", "30/12/2012"));
    v.add(new vencimentos("20/04/2012", "30/12/2012") );
    JRDataSource deps = new JRBeanCollectionDataSource(v);
    p.put("subVencimento",deps);

    mas gera um erro e nao sei como seguir se puder me ajudar fico grato

    ResponderExcluir
    Respostas
    1. Vc criou o parâmetro com o nome "subVencimento"?

      Excluir
  7. Muito bacana suas explicações, mas tenho uma duvida posso passar nois parametros do visual para o Relatorio tipo
    parametros.put( "data_inicio", campoFiltro.getText() );
    parametros.put( "data_final", campoFiltro.getText() );

    e tenho outra duvida não consigo passar uma data como parametro não sei como devo escrever.

    se puder me ajudar agradeço

    Clayton

    ResponderExcluir
    Respostas
    1. Clayton... vc vai precisar criar no relatório parametros com o mesmo nome dos parametros que vc estiver passando. Caso vc crie um parametro com o nome data_inicio do tipo Date, aí vc precisa passar um parametro com data_inicio do tipo Date também.

      Excluir
    2. Andi eu criei o parametro no relatorio do tipo Date, mas por exemplo no codigo mesmo parametros.put( "data_final", campoFiltro.getText() );
      não pode ser ".getText" ele da erro. você tem algum exemplo que pode me ajudar? pois necessito demais disto para você ter ideia preciso apresentar isto hoje a noite e este parametro é o unico que não consigo fazer.

      Excluir
    3. Clayton, quando vc dá um getText() ele retorna apenas uma string, vc precisa transformar isso num Date.

      SimpleDateFormat formatador = new SimpleDateFormat("dd/MM/yyyy");
      Date data = formatador.parse(campoFiltro.getText());

      Excluir
    4. Andi, no parametros.put( "data_final", campoFiltro.getText() ); vou passar assim parametros.put( "data_final", data ); ?

      Excluir
    5. Este comentário foi removido pelo autor.

      Excluir
    6. Andi vou testar aqui, mas de qualquer forma muito obrigado pela sua atenção. tenha uma boa tarde.

      Excluir
  8. Apenas uma sugestão.

    Em vez de passar a lista por parametro, passa por JRBeanCollectionDataSource.

    Todas as outras etapas funcionam normalmente.

    Obrigado, com sua ideia, consegui resolver outra situação.

    Abraço!

    ResponderExcluir
  9. Pessoal to com um problemao aqui ve se alguem consegue me ajudar...
    tenho o seguinte cenario...
    uma lavanderia quand vai fazer uma receita de lavagem (uma receita Contem varias lavagens) e cada lavagem seu array de materia prima que e utilizado.
    resumindo tenho um array de lavagems e dentro de cada lavagem um array de materia prima...
    estou passand um array de lavagems para o Report porem nao estou conseguindo passar o array de materia prima pro subreport...
    alguem pode me ajudar pelo amor de deus ? ><

    ResponderExcluir
  10. Boa tarde pessoal, alguem sabe como manter o SubReport com tamanho estático? ou seja quando atinge o numero de registros dentro da banda Detail do subreport, ele não sobrepor os componentes a baixo, e listar o restante em uma nova pagina.

    ResponderExcluir
  11. excelente tutorial, foi de grande ajuda para min. muito obrigado.

    ResponderExcluir
  12. Como anda? Muy buen aporte, pero tengo un problema. Puedo cargar el subreporte y no el reporte principal desde mi aplicacion.

    ResponderExcluir
  13. O seu tutorial me ajudou muito!

    Consegui achar como corrigir o erro do subreport.
    Nas propriedades do subreport no summary, em Subreport Expression ao invés de colocar apenas $P{LocalizacaoPedidosSubreport}, coloque $P{LocalizacaoPedidosSubreport} + "pedidos_subreport.jasper".
    Aí no método gerarRelatório deixe apenas o path da pasta em que está o .jasper do subrelatório. (src/pedidos/controle/relatorios/)

    ResponderExcluir
  14. Alguém ainda da suporte neste post ? Tenho algumas dúvidas em relação ao relatórios do iReport/Jasper. Todos os exemplos, eu digo TODOS mesmo, que encontrei na web mostram sempre um JRBeanCollectionDataSource recebendo uma função que retorna todos os dados de uma tabela (select *). No entanto, a minha situação é a seguinte: Eu ja possuo um relatório onde busco todos os talhões de uma propriedade, dado o id dessa propriedade. Na chamada ao metodos que geram o relatorio eu tenho o trecho:
    Map parametros = new HashMap<>();
    parametros.put("propriedade", this.param_id);
    JRDataSource jrds = new JRBeanCollectionDataSource(talhaoDao.readTalhaoByProp(param_id));

    Notem que o método que vem da DAO e popula o dataSource, não eh uma lista geral (select *) mas sim um select com uma clausula where. A principio eu não sei se o método na DAO deveria de fato receber este parametro e me retornar apenas os talhoes da propriedade "param_id", ou se ele deveria ser mais genérico e o parametros passado no map restringiria de qual prorpiedade iria retornar os talhoes, meu código parece redundante e ate que o map dos parametros é inútil, o legal é que assim funciona.

    O problema ta em um outro relatório que devo gerar, neste outro relatorio eu preciso mostrar para um determinado intervalo de talhoes em determinada propriedade, todos os plantios e erradicações existentes. Em outras palavras, eu preciso de um relatorio que tenha como parametros o id da propriedade que desejo avaliar, além de um intervalos entre talhaoes (definidos pelo "cod_talhao"). O chefe aqui deseja que neste relatorio seja impresso o cabeçalho do primiero relatorio (mais geral, de talhaoes por propriedade.) juntamente com as informações do plantio e erradicação dos talhaoes.

    Imaginei o seguinte, um relatorio principal identico ao relatorio que ja tenho funcionando e um subRelatorio com as informações dos plantios e erradicações, a questão é que este relatorio esta vindo em branco, acredito que os parametros nao são lidos, ou minha busca no banco esta errada, tive que repetir a busca de talhoes porem com mais parametros, como talhaoDao.readTalhaoByProp(param_id, talhao_INI, talhao_FIM), também acrescentei esses parametros no map, e foi ai que comecei a pensar que estou usando errado o jasperReport.


    Alguém pode me dar uma luz ?

    Att,
    Rafael

    ResponderExcluir
  15. Muito bom o post, apenas duas observações:
    correto é: new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource e não: new net.nf.jasperreports.engine.data.JRBeanCollectionDataSource, quando usar um JRDatasource expression.

    Outro ponto que tive que me certificar é de colocar o caminho absoluto do relatório, não basta uma referência para a pasta apenas ...

    ResponderExcluir
  16. Parabéns pelo tutorial.
    Eu gostaria de saber se eu consigo dentro do parâmetro passar um lista de opções. Tipo, clicar em uma alça lateral, como se fosse em alguns sites mesmo e nele eu conseguir colocar opções tipo : Data de vencimento, Dt. Emissão,etc" para que a possa possa escolher a opção desejada e assim gerar o relatório.

    ResponderExcluir

Deixe seu comentário... ;)