25 de maio de 2011

JasperReports 4.0.1: JDBC datasource - abrir relatório em projeto web

Dando continuidade na postagem: JasperReports 4.0.1: Utilizando JDBC datasource, agora vamos usar o .jasper gerado no iReport, em um projeto Java EE 6.

Começando...
Para começar, no NetBeans crie uma nova Aplicação Web com o nome de RelatorioJDBC (seguindo os passos já vistos em postagens anteriores), o estrutura final é este aqui:


Feito isso, vamos começar a estruturar nosso projeto para usarmos .jasper (criado na postagem do link). Como estamos usando JDBC, precisamos ter uma conexão com o banco de dados, então em pacotes de código-fonte crie um novo pacote com o nome de dao, dentro desse pacote crie a classe Java com o nome de Conexao (já foi utilizada em outras postagem que não utilizava JPA), segue o código-fonte:
public class Conexao {
    private String driver = "com.mysql.jdbc.Driver";
    private String URL = "jdbc:mysql://localhost/5tads";
    private String USER = "root";
    private String SENHA = "admin";
    private Connection conn;

    public Conexao() {
        try {
            Class.forName(driver);
            conn = (Connection) DriverManager.getConnection(URL, USER, SENHA);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Connection getConn() {
        return conn;
    }

    public void fecharConexao() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
Obs: só lembrando que na URL, o 5tads corresponde à minha database.
Como nosso foco não é a conexão com o banco de dados, vamos prosseguir... agora no mesmo pacote dao, vamos criar mais uma classe Java com o nome de ClienteDAO:
public class ClienteDAO {

    private Conexao conexao;
    private Statement stmt;

    public ClienteDAO() {
        conexao = new Conexao();
        try {
            stmt = (Statement) conexao.getConn().createStatement();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }

    public ResultSet clientesResultSet() {
        ResultSet rs = null;
        try {
            rs = stmt.executeQuery("SELECT * FROM cliente ORDER BY nome");
        } catch (Exception e) {
            e.printStackTrace();
        } 

        return rs;
    }
}
Veja que o nosso método clientesResultSet, retorna um ResultSet mesmo e não uma lista de clientes, ao usarmos a metodologia de JDBC datasource, é isto que precisamos passar para o .jasper, e não uma lista como na JavaBean datasource.

Agora vamos colocar o .jasper dentro do nosso projeto, para isso em Páginas Web, dentro de WEB-INF crie um novo diretório com o nome de relatorios, e dentro dele coloque o arquivo .jasper, no meu caso tem o nome de relatorioJDBC.jasper (em caso de dúvidas volte na imagem acima da estrutura do projeto).
O próximo passo é colocar as bibliotecas necessárias no projeto, caso não tenha baixado as bibliotecas do JasperReports, baixe por aqui. De todas as bibliotecas disponibilizadas no link, para este tipo de relatório iremos usar apenas algumas delas, acabei filtrando e chegando as que realmente precisavam, então adicione ao projeto as seguintes bibliotecas:


* quanto a biblioteca do MySQL é do próprio NetBeans (mas pode ser encontrada facilmente no Google).

Agora que já temos nosso dao pronto, vamos criar um novo pacote para armazenar nossos beans esse pacote será o controle: depois de criado, crie uma nova classe Java com o nome de ClienteBean:
@Named
@RequestScoped
public class ClienteBean {

    private ClienteDAO dao;

    public void gerarRelatorio() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        ServletContext context = (ServletContext) externalContext.getContext();
        String arquivo = context.getRealPath("WEB-INF/relatorios/relatorioJDBC.jasper");

        dao = new ClienteDAO();
        JRDataSource jrRS = new JRResultSetDataSource(dao.clientesResultSet());

        gerarRelatorioWeb(jrRS, null, arquivo);
    }

    private void gerarRelatorioWeb(JRDataSource jrRS, Map<Object, Object> parametros, String arquivo) {
        ServletOutputStream servletOutputStream = null;
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();

        try {
            servletOutputStream = response.getOutputStream();
            JasperRunManager.runReportToPdfStream(new FileInputStream(new File(arquivo)), response.getOutputStream(), parametros, jrRS);
            response.setContentType("application/pdf");
            servletOutputStream.flush();
            servletOutputStream.close();
            context.renderResponse();
            context.responseComplete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Vamos ver o que esses métodos fazem:

public void gerarRelatorio()
Inicialmente este método será chamado pela nossa página JSF, nele nós precisamos pegar o caminho exato do nosso arquivo .jasper, que lá no começo da postagem colocamos dentro de WEB-INF/relatorios, para isso usamos o context.getRealPath.
Quanto ao jrRS: por termos utilizado JDBC datasource para gerar nosso .jasper, nos precisamos passar para nosso relatório um JRDataSource, que é formado por um Result Set  que é retornado pelo nosso método lá do ClienteDAO.
Ao chamar o gerarRelatorioWeb, estamos passando um parâmetro null, esse corresponderia a uma Map, que serviria para passarmos parâmetros(que vai ficar pra outra postagem) para o nosso relatório...
Agora vamos ver o que acontece no:
gerarRelatorioWeb(JRDataSource jrRS, Map<Object, Object> parametros, String arquivo)
Neste método nós dependemos do response para mostrar o nosso relatório no navegador, quanto a essa parte não vou dar muitas explicações, pois sairia do nosso foco que é o JasperReports...
Bom, no final o responsável por gerar o que vai ser o nosso relatório, é essa linha aqui:
JasperRunManager.runReportToPdfStream... 
esse método não cria um arquivo .pdf fisicamente no computador, apenas gera ele como se fosse um arquivo temporário, e quem decide se quer salvar o relatório ou não é o usuário, depois disso, o response entra em campo novamente dizendo ao navegador que o que será enviado é do tipo application/pdf, cada navegador reage de uma forma quando se trata de PDF, no caso do Google Chrome, mostra nele mesmo, se não me engano o Firefox, já dá a opção de fazer download do PDF...
e por fim o servletOutputStream, é quem manda o relatório para o navegador!


Depois de entendermos como funciona essa classe, vamos alterar nossa página index.xhtml, para que ela chame o método e mostre o relatório:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:commandButton value="Ver relatorio" actionListener="#{clienteBean.gerarRelatorio}" onclick="this.form.target='_blank'" />
        </h:form>
    </h:body>
</html

Quanto ao onclick do commandButton, eu uso para poder abrir o relatório em outra aba do navegador. Visualmente a página index, fica assim:


Quem estiver usando o Google Chrome para fazer estes testes, terá o seguinte resultado ao clicar no botão:


E por aqui finalizamos a postagem de como usar um .jasper feito com base em JDBC, em um projeto web (Java EE 6)  =)

10 comentários:

  1. Grato por compartilhar seu conhecimento.

    ResponderExcluir
  2. Bom dia andii,
    criei um relatório de acordo com o seu tutorial e deu certinho,
    mas tenho uma duvida. Como faço pra salvar ele como pdf, eu uso o Chrome e quando clico na opção salvar, ele abre a janela para selecionar o arquivo mas o arquivo está com a extensão erra, ele mostra relatorio.jsf.
    Pode me dar alguma dica de como resolver esse problema?

    Obrigada

    ResponderExcluir
    Respostas
    1. Olá Mylla! Também passo por isso, mas acabei nem indo atrás de uma solução pra isso. Ainda não vi necessidade de fazer isso. :S

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

      Excluir
  3. Minina, Eu te amo....rsrs....Seus tutoriais sobre Jasper report estão me ajudando muito no TCC......1000000 pra ti.....Continua assim....

    ResponderExcluir
  4. Ola, consigo apenas gerar se retornar um ResultSet? no meu caso meu Dao retorna uma List
    como deveria proceder?

    ResponderExcluir
  5. Parabéns pelo post! Objetivo e bem explicado.

    ResponderExcluir
  6. Se for feito com hibernate o que deve mudar?

    ResponderExcluir

Deixe seu comentário... ;)