Desenvolva aplicações web com JSF, MongoDB e JasperReports

A quantidade de informações a armazenar cresce de forma exponencial a cada dia. Neste cenário, para que seja possível viabilizar a persistência de tanto conteúdo, é necessário que existam soluções capazes de lidar com esses grandes volumes de dados, atualmente gerados por milhões de usuários. Para tanto, devido às grandes limitações encontradas em bancos de dados relacionais para lidar com quantidades massivas de dados, corporações como o Google, Amazon, Facebook, entre outras, lideram pesquisas para o desenvolvimento de soluções não relacionais, mais popularmente conhecidas como NoSQL, com o intuito de construir opções que não possuam tais restrições. A estrutura flexível proporcionada pelo modelo não relacional possibilita aos administradores de bancos de dados maior praticidade com a escalabilidade, o que, dentre outros fatores, tem sido um dos que mais atraem as empresas a aderirem a este modelo. Como um exemplo, a Globo.com, responsável pelo fantasy game CartolaFC, que já possui mais de dois milhões de usuários, optou pela adoção do banco de dados MongoDB com o intuito de obter alta performance e disponibilidade. Dentre algumas das vantagens de se utilizar o NoSQL, podemos destacar: • A capacidade de armazenar e processar grandes quantidades de dados; • Maior praticidade com a escalabilidade; • Possui diversos bancos de dados gratuitos, sendo os mais populares o MongoDB, HBase e Apache Cassandra.

Como desvantagem, vale ressaltar a enorme dificuldade em encontrar profissionais que estejam aptos a trabalhar com NoSQL. Por ser uma tecnologia nova, grande parte dos especialistas com experiência em bancos relacionais possui conhecimento limitado a respeito das soluções NoSQL. Dito isso, aprenderemos neste artigo que não há nenhuma grande complicação ao utilizar um banco não relacional. Para tanto, vamos implementar uma aplicação JSF que possui um formulário de cadastro e utilizar o MongoDB como ferramenta para viabilizar a persistência dos dados. Em seguida, pensando em enriquecer ainda mais o exemplo, para exibir os dados armazenados vamos construir um relatório com a ferramenta iReport. Ressaltamos que a intenção do artigo não é comparar o modelo não relacional com o modelo relacional, que por mais de quarenta anos tem sido a principal opção para o armazenamento de dados, mas sim demonstrar algumas características do banco de dados não relacional MongoDB e seu uso em uma aplicação JSF com relatórios JasperReports.

Sobre o MongoDB

O MongoDB é um dos vários bancos de dados criados nos últimos anos sob o conceito de bancos orientados a documentos. Este conceito tem como principal característica utilizar uma estrutura baseada em coleções e documentos para armazenar os dados. Assim, ao contrário dos bancos de dados relacionais, onde trabalhamos com tabelas, neste modelo lidamos com coleções. A Tabela 1 traz uma comparação dos termos utilizados no modelo relacional e no MongoDB. Como um dos grandes diferenciais, destaca-se que no modelo relacional é sugerido que a redundância de dados seja evitada, pois os relacionamentos entre entidades são uteis justamente para evitar a redundância de informações. Já no MongoDB ocorre exatamente o contrário: não existem relacionamentos e com isso a redundância de informações é incentivada. Deste modo, em um documento devem estar todas as informações do elemento persistido, mesmo que elas sejam redundantes. Isto evita a necessidade de realizar joins para obter as informações em uma coleção, o que resulta em ganho de performance.

Sobre o JasperReports e iReport

O JasperReports é uma biblioteca open source utilizada para a geração de relatórios na plataforma Java. Como grande vantagem, fornece o recurso de exportar os relatórios em diversos tipos de arquivos, como PDF, XML e XLS. Além disso, é possível agrupar outros relatórios em um único com a opção de criação de sub-relatórios para melhor organizar as informações. Outra característica interessante é a possibilidade de trabalhar com diversos bancos de dados que permitem efetuar conexões via JDBC, incluindo bancos não relacionais, como MongoDB e HBase. Com a utilização do JasperReports, toda a estrutura de um relatório é definida em um arquivo XML, geralmente com a extensão .jrxml. Desta forma, permite ao desenvolvedor realizar modificações diretamente no código fonte do relatório, caso seja de sua preferência. Já o iReport é uma ferramenta gráfica utilizada para a elaboração de relatórios que utilizam a biblioteca JasperReports. Esta ferramenta fornece diversos componentes que possibilitam a redução do tempo para desenhar os relatórios como, por exemplo, subreports, charts, barcodes, etc. Ademais, o iReport também disponibiliza diversos templates, que podem ajudar e poupar o tempo do desenvolvedor.

A aplicação exemplo

No decorrer do artigo, como já mencionado, apresentaremos o desenvolvimento de uma aplicação MVC com JavaServer Faces e MongoDB. Para isso, construiremos um formulário simples com o intuito de cadastrar informações de venda de produtos. Pensando na exibição de tais informações, será demonstrado como recuperar dados do MongoDB e criar um relatório com iReport, quando abordaremos também o uso de alguns dos seus vários componentes, como o subreport.

Criando a aplicação

Para iniciar a implementação do nosso exemplo é necessário que você tenha o ambiente de desenvolvimento configurado. Neste caso, vamos adotar o Eclipse Indigo como IDE e o iReport para o desenvolvimento do relatório. Portanto, baixe e instale estas ferramentas, cujos endereços para download podem ser encontrados na seção Links. O MongoDB também pode ser baixado do site oficial do projeto. Lá você notará que existem versões compatíveis para Windows, Linux, Mac e Solaris. Aqui, iremos baixar a versão compatível para o Windows. Para instalá-lo basta seguir as etapas sugeridas durante a execução do wizard. Encerrada essa etapa podemos, enfim, criar o projeto. Portanto, abra o Eclipse, acesse o menu File > New e, em seguida, escolha a opção Dynamic Web Project. Feito isso, acesse a pasta bin do MongoDB (em nosso exemplo, o diretório a ser acessado pelo terminal de comando é C:\mongodb\bin), digite “mongo” e pressione Enter para abrir o terminal onde poderemos gerenciar o banco de dados exemplo. Feito isso, digite use test no terminal para informar que iremos utilizar a base nomeada como test. Esta base é criada durante o processo de instalação do MongoDB. Logo após, digite o código exibido na Listagem 1 para criar as collections de produtos, clientes e vendas, que serão adotadas em nosso exemplo. Assim, teremos o banco de dados pronto. Neste exemplo, iremos inserir produtos, clientes e registros de venda através da aplicação pela interface web, mas caso seja do seu interesse, você também pode inserir as informações diretamente no terminal, via linha de comando. O código de exemplo para isso pode ser encontrado a partir da linha 6. Por fim, antes de iniciar o desenvolvimento, é preciso que você realize o download de mais algumas bibliotecas, a saber: mongojava-driver.jar, para possibilitar a conexão com o banco de dados e a biblioteca primefaces.jar, para utilizarmos alguns componentes visuais do PrimeFaces (veja os endereços indicados na seção Links). Estes arquivos devem ser adicionados na pasta lib do projeto.

Conexão com o banco de dados

Com o projeto e o banco de dados criados, vamos configurar a conexão entre nossa aplicação e o MongoDB. Para isso, crie a classe ConexaoMongo com o código apresentado na Listagem 2. Nesta classe implementamos o método getConnection() e nele solicitamos por parâmetro o nome da collection em que serão realizadas as operações de cadastro ou consulta. Após isso, é criada a conexão na linha 14, onde informamos o endereço do banco de dados e a porta de acesso, que por padrão é a 27017. Por fim, informamos ao método getCollection() – na linha 15 – o nome da collection a ser utilizada.

Criando o Managed Bean

Após criar a classe de conexão com o banco de dados, vamos implementar os métodos responsáveis por invocar os métodos de inserção e consulta de informações no banco de dados. Para isso, crie a classe VendasBean, cujo código é apresentado na Listagem 3. Neste código temos como primeiro método o init(), e dentro dele invocamos os métodos limparTela(), listarClientes() e listarProdutos(). Antes disso, no entanto, é verificado na linha 12 se a página não é um postback, ou seja, se a página não é fruto de um envio de formulário, por exemplo. Caso não seja, os métodos citados anteriormente são executados, pois isto só pode acontecer quando for o primeiro carregamento da página feito pelo usuário.

Estes métodos, como indicado pelo nome, serão responsáveis por limpar os campos da tela no primeiro acesso e buscar clientes e produtos no banco de dados. O conteúdo retornado será listado de forma separada no componente na camada view. Após isso, implementamos os métodos novoProduto(), novoCliente() e novaVenda(). Estes possuem a mesma estrutura interna e executarão os respectivos métodos – das classes ProdutoService, ClienteService e VendaService da camada de serviço – para inserir informações no banco de dados. Por fim, criamos os métodos listarVendas(), listarProdutos() e listarClientes(), que irão buscar os valores inseridos nas respectivas collections.

Camada de serviço

No passo anterior definimos os métodos que serão invocados pela camada view e serão responsáveis por realizar a chamada dos métodos da camada de serviço. Para isso, criamos as classes ProdutoService, ClienteService e VendaService. Nelas estarão contidos os métodos que efetivamente irão realizar a busca e inserção de dados no banco. A Listagem 4 exibe o código da classe ProdutoService. Os procedimentos para efetuar a consulta e o cadastro de produtos, clientes e vendas são idênticos. Portanto, explicaremos apenas o código de dois deles: inserirProduto() e buscarProdutos(). No método inserirProduto(), de forma objetiva, adicionamos um registro de produto na devida collection.

Para isso, ele recebe como parâmetro o objeto Produto com as informações que serão gravadas. Na primeira instrução, obtemos a conexão com nossa base de dados. Em seguida, instanciamos um objeto da classe BasicDBObject, que nos permite inserir um novo documento na base de dados. Deste modo, após obter a instância, informamos através do método put() os campos que serão inseridos e seus devidos valores – neste caso, os valores carregados no objeto Produto. Por último, efetuamos o cadastro do novo registro ao executar do método insert(). O próximo método a ser analisado é buscarProdutos(). Em seu código, estabelecemos a conexão com o banco ao chamar getConnection() e instanciamos o objeto BasicDBObject. Em seguida, através do método put() informamos ao objeto instanciado o atributo que é recuperado ao chamar prod.getAtivo(), indicando que devem ser buscados todos os produtos que estiverem com o status ativo (vide linha 25). Logo após, na linha 27, é efetuada a consulta através do método find(). Após isso, os valores retornados são armazenados no objeto cursor. Para que estes dados do documento possam ser exibidos em forma de lista, criamos o método converteDocumentoParaMap(), que será invocado a cada iteração do bloco while na linha 30. Este procedimento é necessário porque não há como exibir diretamente o conteúdo do objeto cursor em uma lista. Na linha 36 criamos um novo objeto e o nomeamos como produto, o qual será preenchido com os dados convertidos pelo método converteDocumentoParaMap(). Entre as linhas 38 e 55 é realizada uma verificação para que os valores das colunas da collection sejam atribuídos ao seu devido atributo do objeto produto. Por fim, adicionamos este objeto a uma lista na linha 57.

Criando um relatório com iReport

Antes de iniciarmos a criação do relatório é necessário estabelecer a conexão entre o banco de dados e o iReport, para possibilitar a busca de informações que serão visualizadas durante o desenvolvimento na IDE. Para isso, após iniciar o iReport, clique no botão que simboliza uma tomada na tela inicial e depois em New, para criar uma nova conexão. Em seguida, será apresentada uma lista com os tipos de conexão que o iReport aceita; escolha a opção MongoDB Connection. Para finalizar, preencha os campos Mongo URI com a URL mongodb://127.0.0.1:27017/test e deixe vazio os campos User Name e Password, pois estamos utilizando o banco de dados test, que por padrão não necessita de informações de login. Caso todos os passos tenham sido realizados corretamente, após clicar no botão Test deverá aparecer a mensagem Connection Test Successful. Com a conexão estabelecida, podemos iniciar a criação de um relatório que irá se conectar ao MongoDB para buscar as informações a serem apresentadas. Portanto, clique em New no menu principal do iReport, escolha o modelo de relatório a ser utilizado (em nosso caso selecionamos o modelo em branco) e clique em Open This Template. Com isso, o relatório modelo já estará criado e, a partir de então, conseguiremos dar início ao desenvolvimento do mesmo. Após criar o relatório veremos uma tela semelhante à da Figura 1. Neste momento, clique no ícone ao lado direito da palavra Preview e inclua a query {‘collectionName’ : ‘clientes’, ‘findQuery’ : {‘codigo’ : {‘$eq’ : $P{pCodigoCliente}}}}. Esta query trará todos os clientes cadastrados em nosso banco de dados mediante o código informado no parâmetro pCodigoCliente. Conforme explicado anteriormente, quando criamos um relatório no iReport, um arquivo XML com o código do relatório é criado. A partir de então, todo procedimento realizado visualmente, como a adição de uma caixa de texto, é mapeado para adição/alteração/remoção do código relacionado neste arquivo. Apesar desta facilidade, caso prefira, o desenvolvedor também pode trabalhar diretamente no XML. O código fonte do relatório pode ser acessado através da opção XML (vide Figura 2).

Para apresentar os dados retornados pela query, utilizamos o componente TextField. Nele, definimos no parâmetro Text Field Expression de qual coluna da collection desejamos que as informações sejam exibidas. Após montar o relatório, clique em Preview para compilá-lo e ver como ele ficou. A Figura 2 exibe o resultado.

Criando sub-relatórios

Uma das melhores funcionalidades fornecidas pelo iReport é a possibilidade de incluir sub-relatórios em um relatório principal. Este recurso é bastante útil caso o desenvolvedor queira organizar melhor a apresentação dos dados. Além disso, muitas vezes também precisamos exibir valores provenientes de queries que não possuem relações (joins) com a query do relatório principal. Nestes casos, o uso de sub-relatórios também se torna um grande diferencial. Para incluir um sub-relatório ao relatório principal, acesse a paleta de ferramentas do JasperReports, selecione o componente SubReport e o arraste até a aba Summary. O resultado será semelhante ao exibido na Figura 3. Em seguida, será exibida a tela onde você deverá configurar o sub-relatório, informando o nome e modelo de sub-relatório que deseja utilizar, a query que irá recuperar os dados a serem exibidos e se você deseja utilizar a mesma conexão que o relatório principal adota para se conectar ao banco. Neste exemplo faremos uso da mesma conexão. A query a ser incluída no sub-relatório é apresentada a seguir: {‘collectionName’ : ‘vendas’, ‘findQuery’ :

{‘codigocliente’ : {‘$eq’ : $P{pCodigoCliente}}}}

Esta query, que irá retornar os produtos vendidos, recebe por parâmetro o mesmo valor informado no parâmetro pCodigoCliente da query responsável por retornar o cliente, pois desta forma retornará apenas os produtos vendidos para este mesmo cliente. Para isso, acesse as propriedades do sub-relatório, procure pela propriedade parameters e clique duas vezes sobre ela. Logo após, clique no botão add e informe o nome do parâmetro como pCodigoCliente. No campo Expression, informe $P{pCodigoCliente}, conforme exibe a Figura 4.

Assim, caso você tenha inserido os registros manualmente, como mencionado no início do artigo, ao gerar o relatório devem ser exibidos o cliente – cujo o código é o informado na passagem de parâmetro – e os produtos vendidos para ele. Após montar o relatório, clique em Preview para compilá-lo e ver o resultado. A Figura 5 exibe o relatório com o sub-relatório gerado.

Gerando o relatório pela aplicação JSF

Com a estrutura do projeto pronta, implementaremos agora os métodos necessários para a execução do relatório Jasper através da aplicação JSF. Para isso, criaremos um servlet a ser executado diretamente do HTML e uma classe para auxiliar o servlet a gerar o relatório. As Listagens 5 e 6 exibem, respectivamente, o código do servlet, que chamamos de RelatorioServlet, e o código da classe auxiliar, RelatorioUtil.

No código do servlet (vide Listagem 5), definimos três variáveis do tipo String: duas para armazenar o caminho dos arquivos relatorio.jasper e relatorio.jrxml, que serão necessários para gerar o relatório pela aplicação; e uma para armazenar o nome do arquivo a ser gerado para o usuário (neste caso, relatorio.pdf). Para obter o caminho destes arquivos, utilizamos o método getRealPath(). Este retornará o diretório e, então, concatenamos com o nome dos arquivos desejados (relatorio.jasper e relatorio.jrxml). Logo após, instanciamos um objeto do tipo FileInputStream informando como parâmetro o caminho do arquivo relatorio.jasper, que contém os dados (bits) necessários para a classe JasperPrint preencher o relatório (veremos como isso funciona adiante).

Estabelecemos, então, a conexão com o banco de dados para que a query que incluímos no relatório seja executada e os dados sejam apresentados no PDF. Para isso, definimos em uma variável do tipo String a URL do banco de dados e informamos esta variável como parâmetro para instanciar a classe MongoDbConnection, que além deste parâmetro recebe o nome de usuário e a senha para autenticação. Como neste exemplo não utilizaremos credenciais para autenticação, podemos informar os parâmetros de usuário e senha como null. Por fim, chamamos o método createPDFReport(), proveniente da classe RelatorioUtil. Esta, por sua vez, tem o objetivo de gerar o relatório e disponibilizá-lo para o usuário, seja em forma de download, onde o arquivo é baixado para o computador, seja abrindo-o diretamente no browser. Ambas as opções podem ser definidas no código desta classe. No código da Listagem 6, temos apenas o método createPDFReport(), onde definimos algumas configurações para a geração do relatório. Neste método, definimos na linha 10 que o tipo de arquivo a ser gerado será um PDF e na linha 13 forçamos o download deste arquivo, ou seja, ele não será exibido no browser. Contudo, caso opte pela exibição no browser, comente essa linha de código. Em seguida, na linha 19, criamos o JasperPrint, que gera a versão do relatório preenchida. Esse objeto representa o relatório finalizado, e a partir dele podemos enviar para impressão diretamente ou exportar para outro formato, tal como o PDF. Em nosso código, exportamos o relatório através das linhas 23 a 31.

Criando a interface web

Com o banco de dados pronto e as classes necessárias para o funcionamento da aplicação implementadas, criaremos neste momento a interface web. Para isso, importe para o projeto as bibliotecas informadas no início do artigo e crie uma página XHTML. Nesta página, adicionaremos alguns componentes do PrimeFaces para cadastrar e consultar as informações armazenadas no MongoDB (vide Figura 6). A Listagem 7 exibe a primeira parte do código XHTML, local onde serão exibidos os filtros responsáveis pelas consultas e os botões que irão abrir os formulários de cadastro e a lista vendas. No código exibido nessa listagem, definimos a estrutura da página principal onde iremos realizar a consulta e exibição das vendas cadastradas.

Neste bloco, inicialmente utilizamos o componente , nas linhas 14 a 17, para executar ações (métodos) ao iniciar o carregamento da página; neste caso, ele irá executar o método init(), que é responsável por chamar a busca de produtos e clientes, informações que serão úteis para a consulta das vendas. Em seguida, na linha 19, declaramos o componente para exibir mensagens ao usuário após realizar o cadastro de produtos, clientes e ou vendas como, por exemplo: “Venda cadastrada com sucesso” e “Erro ao cadastrar venda”. Depois disso, construímos duas estruturas de formulário: uma entre as linhas 21 e 26, que irá conter os componentes para exibir produtos e clientes; e outra entre as linhas 30 e 59, que contém o componente para listar as vendas cadastradas. Após criar o layout da página principal, implementaremos os filtros para realizar a consulta das vendas. Sendo assim, adicionaremos o componente para exibir produtos e clientes que o usuário poderá selecionar para efetuar a consulta, como expõe a Listagem 8. Vale ressaltar que este código deve ser inserido entre as linhas 22 e 24 da listagem anterior.

Demonstramos nesse bloco a utilização de dois componentes : um para listar os clientes e outro para listar os produtos cadastrados no banco de dados. A partir disso, após o usuário selecionar o item em algum dos dois componentes, o método listarVendas(), especificado nas linhas 07 e 17, será acionado. Feito isso, a busca dos dados será realizada e o resultado atribuído à lista listaVendas. Para fazer com que o conteúdo dessa lista seja exibido no , informamos no atributo value deste componente a lista que possui os dados das vendas, conforme a linha 35 da Listagem 7. Já na Listagem 9 declaramos três componentes , que serão utilizados para abrir os formulários onde iremos cadastrar os produtos, os clientes e as vendas. Em cada um deles, definimos que ao completar a ação de chamada do botão (veja as linhas 2, 9 e 17), o formulário será exibido através do comando PF().show(). Além disso, declaramos também o botão que irá realizar a chamada do servlet responsável pela impressão do relatório. Este código deve ser inserido na linha 31 da Listagem 7.

Por sua vez, a Listagem 10 apresenta o código de um dos três formulários criados para cadastro; neste caso, o código do formulário para cadastro de produtos. Como todos realizam o mesmo procedimento, tendo praticamente os mesmos códigos, não exibiremos os outros dois. Dentro do formulário, definimos na linha 2 o componente , utilizado para exibir o formulário para o usuário em uma tela de diálogo, conforme exibe a Figura 7. Após isso, declaramos dois componentes , onde serão informados pelo usuário a descrição e o preço do produto, e um componente , onde serão exibidas as opções “Ativo” e “Inativo”, para que o usuário defina se o produto está disponível ou não para ser vendido. Por fim, adicionamos dois botões, sendo o primeiro para cancelar a operação e o segundo para salvar o produto. Este código deve ser inserido após a linha 59 da Listagem 7.

A partir do conteúdo apresentado o leitor será capaz de desenvolver suas primeiras aplicações web utilizando como solução de persistência o banco de dados MongoDB. Como desafio, tente adicionar novos recursos à aplicação exemplo, como novos tipos de consulta, enriquecer o relatório, entre outras funcionalidades. Isso o estimulará a pesquisar por outros recursos do MongoDB e solidificar o assunto abordado. Lembre-se que a prática também é fundamental para que saibamos como explorar, da melhor maneira, os recursos no código.

Links: Endereço para download do PrimeFaces. http://www.primefaces.org/downloads

Endereço para download do MongoDB. http://docs.mongodb.org/ecosystem/drivers/downloads

Endereço para download do driver do MongoDB para Java. http://mongodb.github.io/mongo-java-driver

Endereço para download do iReport. http://community.jaspersoft.com/project/ireport-designer/releases

Endereço para download do Eclipse. https://eclipse.org/downloads

0
Subscribe to my newsletter

Read articles from Luis Gustavo Souza directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Luis Gustavo Souza
Luis Gustavo Souza