quinta-feira, 13 de maio de 2010

User Interface Composition (UI composition)

O reaproveitamento de código com facelets é bem intuitivo para quem já trabalha com gerenciadores de conteúdo. Como sempre, é viável, mas tem boas e más notícias.
Como exemplo, podemos criar um dataPaginator que pode ser reaproveitado em outras páginas. Esse é um bom exemplo, pois o código do dataPaginator é bem extenso (ver o artigo http://rfavero.blogspot.com/2010/04/criar-datatable-e-colocar-um-paginador.html).
Para começar, criamos uma pasta dentro de Páginas Web chamada templates, por exemplo. Nela, colocamos um novo arquivo JSF chamado aqui de paginator.

O conteúdo do arquivo paginator.xhtml deve ser modificado desta maneira:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:ice="http://www.icesoft.com/icefaces/component" xmlns:f="http://java.sun.com/jsf/core">
    <body>
        <ui:composition>
        </ui:composition>
    </body>
</html>

Todo o código colocado entre as tags composition será reaproveitado em outra página. Qualquer código fora delas será ignorado.
No arquivo pessoas.xhtml, devemos colocar (se não houver), um id para o formulário:
01  <body>
02      <ice:form id="edit_form">
03          <div align="center">

O código entre as tags <ice:dataPaginator> e </ice:dataPaginator>  (incluindo as mesmas) do arquivo pessoas.xhtml deve ser transferido para o arquivo paginator.xhtml, dentro das tags composition. O início do dataPaginator deve ser modificado para que fique assim:

01 <ui:composition>
02     <ice:dataPaginator for="#{tabela}"
03                        paginator="true"
04                        fastStep="5"
05                        paginatorMaxPages="#{paginas}">
06         <f:facet name="first">
07             <ice:graphicImage

Nas linhas 02 e 05 podemos observar os parâmetros que deverão ser passados pela página que vai utilizar este template. O dataPaginator já foi retirado da página pessoas.xhtml, e no lugar dele vamos inserir o código, destacado em negrito:

01  <ice:panelTab id="panelTab1" label="Lista">
02   <ui:include src="templates/paginator.xhtml">
03       <ui:param name="tabela" value="edit_form:painel:0:tabelaPessoas" /> 
04       <ui:param name="paginas" value="5" /> 
05   </ui:include>
06      <ice:dataTable id="tabelaPessoas"

Na linha 02 está o caminho para o arquivo que vai ser reutilizado, com a tag ui:include. Nas linhas 03 e 04, as declarações dos parâmetros e seus valores. Boas notícias: o código diminui drasticamente, fica mais claro e podemos reaproveitar a pasta templates em outros projetos. A má notícia: podemos observar que na linha 03, o parâmetro que se refere ao componente dataTable deve indicar o id que será gerado pelo iceFaces! Esse id pode ser conseguido olhando o código fonte da página pessoas.xhtml, antes de retirar o dataPaginator.

quinta-feira, 6 de maio de 2010

Validação de formulários

Uma maneira elegante de controlar erros nos formulários construídos com IceFaces é criar uma classe no modelo, onde são criados atributos do tipo String que servirão para controlar a classe CSS utilizada para formatar o erro e a mensagem de erro que será mostrada.
No caso da entidade Person, supondo que apenas os atributos name e lastUpdated serão validados, a classe Erro poderia ser construída como segue: 

01 package modelo;
02 
03 public class Erro {
04 
05     private String name;
06     private String lastUpdated;
07 
08     public Erro() {
09     }
10 
11     public String getLastUpdated() {
12         return lastUpdated;
13     }
14 
15     public void setLastUpdated(String lastUpdated) {
16         this.lastUpdated = lastUpdated;
17     }
18 
19     public String getName() {
20         return name;
21     }
22 
23     public void setName(String name) {
24         this.name = name;
25     }
26 }
No controlador, esse modelo é utilizado para declarar dois novos objetos: 
 
private Erro erros = new Erro();
   private Erro classeErros = new Erro();

É necessário criar os getters e setters para os dois novos objetos. Em seguida, esses objetos serão utilizados ao validar uma instância da classe pessoa:


   private boolean pessoaValida() {
        erros = new Erro();
        classeErros = new Erro();
        boolean naoOcorreuErro = true;
        if (pessoaSelecionada.getName().isEmpty()) {
            erros.setName("Nome é obrigatório.");
            classeErros.setName("classeErro");
            naoOcorreuErro = false;
        }
        if (pessoaSelecionada.getLastupdated() == null) {
            erros.setLastUpdated("Data de atualização é obrigatória.");
            classeErros.setLastUpdated("classeErro");
            naoOcorreuErro = false;
        }
        return naoOcorreuErro;
    }

Um exemplo de validação pode ser implementado no momento de gravar uma alteração:
public void atualizar() {
        if (pessoaValida()) {
            boolean gravou = dao.crud(pessoaSelecionada, "3");
            abaSelecionada = 0;
        }
    }

Também é necessário um arquivo CSS com a classe de estilo do erro que sobrescreve, com a cláusula !important, os atributos CSS utilizados pelo componente panelGroup (pode ser criada uma pasta resources e um arquivo estilos.css, como sugestão) ...

.classeErro {
    background-color: #ff9933 !important;
    padding-right: 2px !important;
    padding-left: 2px !important;
    padding-bottom: 2px !important;
    padding-top: 2px !important;
}

...será necessário referenciar o arquivo CSS criado (no meu caso, em pessoas.xhtml)...


<head>
      <ice:outputstyle href="./xmlhttp/css/rime/rime.css" />
      <ice:outputstyle href="/resources/estilos.css" />
</head>

...e modificar os componentes utilizados para a inserção dos dados:

<ice:outputtext value="Nome:" />
<ice:panelgroup styleclass="#{controlador.classeErros.name}">
   <ice:panelgroup>
      <ice:outputtext value="#{controlador.erros.name}" />
   </ice:panelgroup>
   <ice:inputtext value="#{controlador.pessoaSelecionada.name}" />
</ice:panelgroup>




<ice:outputText value="Atualizado em:" />
<ice:panelGroup  styleClass="#{controlador.classeErros.lastUpdated}">
   <ice:panelGroup>
      <ice:outputText value="#{controlador.erros.lastUpdated}" />
   </ice:panelGroup>
   <ice:inputText value="#{controlador.pessoaSelecionada.lastupdated}" >
      <f:convertDateTime pattern="dd/MM/yyyy" />
   </ice:inputText>
</ice:panelGroup>
 
E o resultado final, ao ocorrer uma tentativa de gravação com algum dos campos requeridos em branco, ficará assim:


Esta técnica para mostrar o campo que está impedindo a validação foi desenvolvida com base na técnica utilizada pelo gerenciador de conteúdos Plone, versão 2.5. Não é uma técnica elegante?

quinta-feira, 22 de abril de 2010

O componente rowSelector do IceFaces

A intenção de utilizar rowSelector é que quando o internauta selecionar uma linha do dataTable, o objeto correspondente seja editado automaticamente. Podemos fazer isso preparando o dataTable já existente para funcionar dentro de um panelTabSet. Essa estratégia permitirá que adicionemos um formulário de edição em uma das abas do panelTabSet e enviemos para ele os dados da linha que for selecionada.
No backing bean (ControladorBean.java) serão necessários dois novos atributos:

private int abaSelecionada = 0;
private Person pessoaSelecionada;

Sem esquecer que os gets e sets também serão necessários. Em seguida, é preciso um método que seja disparado quando uma linha for selecionada no dataTable:

    public void selecionouLinha(RowSelectorEvent linha) {
        int linhaSelecionada = linha.getRow();
        lista.setRowIndex(linhaSelecionada);
        pessoaSelecionada = (Person)lista.getRowData();
        abaSelecionada = 1;
    }

Para gravar as alterações, um método que se comunica com a camada de persistência:

    public void atualizar() {
        boolean gravou = dao.crud(pessoaSelecionada, "3");
        abaSelecionada = 0;
    }

E também um método para cancelar a alteração em curso e voltar para a lista de pessoas:

    public void cancelar() {
        pessoaSelecionada = new Person();
        abaSelecionada = 0;
    }

Finalmente, o código completo e atualizado da página (pessoas.xhtml)

001 <?xml version='1.0' encoding='UTF-8' ?>
002 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
003     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
004 <html xmlns="http://www.w3.org/1999/xhtml" 
005       xmlns:ui="http://java.sun.com/jsf/facelets"
006       xmlns:f="http://java.sun.com/jsf/core"
007       xmlns:h="http://java.sun.com/jsf/html"
008       xmlns:ice="http://www.icesoft.com/icefaces/component">
009     <head>
010         <ice:outputStyle href="./xmlhttp/css/rime/rime.css" />
011     </head>
012     <body>
013         <ice:form>
014             <div align="center">
015                 <h1><ice:outputText value="Pessoas" /></h1>
016                 <ice:panelTabSet id="painel" selectedIndex="#{controlador.abaSelecionada}">
017                     <ice:panelTab id="panelTab1" label="Lista">
018                         <ice:dataPaginator for="tabelaPessoas"
019                                            paginator="true"
020                                            fastStep="5"
021                                            paginatorMaxPages="3">
022                             <f:facet name="first">
023                                 <ice:graphicImage
024                                     url="./xmlhttp/css/xp/css-images/arrow-first.gif"
025                                     style="border:none;"
026                                     title="Início"/>
027                             </f:facet>
028                             <f:facet name="last">
029                                 <ice:graphicImage
030                                     url="./xmlhttp/css/xp/css-images/arrow-last.gif"
031                                     style="border:none;"
032                                     title="Final"/>
033                             </f:facet>
034                             <f:facet name="previous">
035                                 <ice:graphicImage
036                                     url="./xmlhttp/css/xp/css-images/arrow-previous.gif"
037                                     style="border:none;"
038                                     title="Anterior"/>
039                             </f:facet>
040                             <f:facet name="next">
041                                 <ice:graphicImage
042                                     url="./xmlhttp/css/xp/css-images/arrow-next.gif"
043                                     style="border:none;"
044                                     title="Próxima"/>
045                             </f:facet>
046                             <f:facet name="fastforward">
047                                 <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-ff.gif"
048                                                   style="border:none;"
049                                                   title="Avanço rápido"/>
050                             </f:facet>
051                             <f:facet name="fastrewind">
052                                 <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-fr.gif"
053                                                   style="border:none;"
054                                                   title="Retrocesso rápido"/>
055                             </f:facet>
056                         </ice:dataPaginator>
057                         <ice:dataTable id="tabelaPessoas"
058                                        value="#{controlador.lista}"
059                                        var="item" border="1"
060                                        rows="3"
061                                        sortColumn="#{controlador.colunaOrdenada}"
062                                        sortAscending="#{controlador.ordemInversa}">
063                             <ice:column>
064                                 <ice:rowSelector selectionListener="#{controlador.selecionouLinha}" />
065                                 <f:facet name="header">
066                                     <ice:commandSortHeader columnName="Nome" arrow="true" >
067                                         <ice:outputText value="Nome"/>
068                                     </ice:commandSortHeader>
069                                 </f:facet>
070                                 <ice:outputText value="#{item.name}" />
071                             </ice:column>
072                             <ice:column>
073                                 <f:facet name="header">
074                                     <ice:commandSortHeader columnName="Tratamento" arrow="true" >
075                                         <ice:outputText value="Tratamento"/>
076                                     </ice:commandSortHeader>
077                                 </f:facet>
078                                 <ice:outputText value="#{item.jobtitle}" />
079                             </ice:column>
080                             <ice:column>
081                                 <f:facet name="header">
082                                     <ice:commandSortHeader columnName="Atualizado em" arrow="true">
083                                         <ice:outputText value="Atualizado em"/>
084                                     </ice:commandSortHeader>
085                                 </f:facet>
086                                 <ice:outputText value="#{item.lastupdated}">
087                                     <f:convertDateTime pattern="dd/MM/yyyy" />
088                                 </ice:outputText>
089                             </ice:column>
090                         </ice:dataTable>
091                     </ice:panelTab>
092                     <ice:panelTab label="Editar">
093                         <ice:panelGrid columns="2">
094                             <ice:outputText value="Nome:" />
095                             <ice:inputText value="#{controlador.pessoaSelecionada.name}" />
096                             <ice:outputText value="Tratamento:" />
097                             <ice:inputText value="#{controlador.pessoaSelecionada.jobtitle}" />
098                             <ice:outputText value="Atualizado em:" />
099                             <ice:inputText value="#{controlador.pessoaSelecionada.lastupdated}" >
100                                 <f:convertDateTime pattern="dd/MM/yyyy" />
101                             </ice:inputText>
102                             <ice:commandButton value="Gravar" action="#{controlador.atualizar}" />
103                             <ice:commandButton value="Cancelar" action="#{controlador.cancelar}" />
104                         </ice:panelGrid>
105                     </ice:panelTab>
106                 </ice:panelTabSet>
107             </div>
108         </ice:form>
109     </body>
110 </html>
 
A linha 64 mostra a utilização do rowSelector, e as linhas 92-105 mostram o formulário de edição. Na linha 16, há a propriedade selectedIndex do panelTabSet que o backing bean utiliza para mudar a aba selecionada, por meio do atributo abaSelecionada do controlador. A seguir, duas imagens que mostram o comportamento do panelTabSet e o formulário de edição recebendo os dados.


Resta somente a perfumaria - mensagens de interação com o usuário, opções para excluir, inserir, filtrar, etc. , que poderão ser assunto de outro post.

quarta-feira, 21 de abril de 2010

Colunas ordenadas no DataTable

Utilizar o componente commandSortHeader do IceFaces pode parecer um pouco confuso no início, mas é um componente muito útil e oferece bastante controle de suas características.
Para utilizá-lo, começamos por modificar as colunas do componente dataTable, dessa maneira:

<ice:column>
   <f:facet name="header">
      <ice:commandsortheader arrow="true" columnname="Nome">
         <ice:outputtext value="Nome"/>
      </ice:commandsortheader>
      <ice:outputtext value="#{item.name}"/>
   </f:facet>
</ice:column>

O valor da propriedade columnname deve ser sempre o título da coluna. Para que o componente dataTable entenda essas mudanças, devemos adicionar a ele algumas propriedades:

<ice:dataTable id="tabelaPessoas"
               value="#{controlador.lista}"
               var="item" border="1"
               rows="3"
               sortColumn="#{controlador.colunaOrdenada}"
               sortAscending="#{controlador.ordemCrescente}">

As duas últimas propriedades é que fazem a diferença. A boa notícia é que o componente se encarrega de enviar as mudanças de coluna que o usuário fizer aos atributos do backing bean (colunaOrdenada e ordemCrescente). Esses atributos serão utilizados para determinar como deve ser feita a ordenação do atributo lista do backing bean, e podem ser declarados como segue, no controlador:
private boolean ordemCrescente = true;
private String colunaOrdenada = "Nome";

É importante perceber que ao ser instanciado, o DataTabe vai ficar ordenado pela coluna "Nome", e qualquer seleção feita pelo usuário será transmitida para o atributo colunaOrdenada. Agora, a má notícia: deve ser implementado um método específico para a ordenação. Eu optei por utilizar um comparator e o método sort da classe java.util.Collections. A seguir, o método utilizado para a ordenação:

01     protected void ordenar(List relacao) {
02         Comparator comparator = new Comparator() {
03 
04             public int compare(Object o1, Object o2) {
05                 Person c1 = (Person) o1;
06                 Person c2 = (Person) o2;
07                 if (colunaOrdenada.equals("Nome")) {
08                     return ordemCrescente
09                             ? (c1.getName()).compareTo((c2.getName()))
10                             : (c2.getName()).compareTo((c1.getName()));
11                 }
12                 if (colunaOrdenada.equals("Tratamento")) {
13                     return ordemCrescente
14                             ? (c1.getJobtitle()).compareTo((c2.getJobtitle()))
15                             : (c2.getJobtitle()).compareTo((c1.getJobtitle()));
16                 }
17                 if (colunaOrdenada.equals("Atualizado em")) {
18                     return ordemCrescente
19                             ? (c1.getLastupdated()).compareTo((c2.getLastupdated()))
20                             : (c2.getLastupdated()).compareTo((c1.getLastupdated()));
21                 }
22                 return 0;
23             }
24         };
25         Collections.sort(relacao, comparator);
26     }

Fica bem claro que para cada coluna, será necessário um tratamento para que o Comparator possa executar as comparações. É importante observar a utilização dos atributos colunaOrdenada e ordemCrescente, que são alterados pela página, conforme as escolhas do usuário. Este método deve ser colocado em algum local onde seja executado quando houver mudança de coluna ou mudança da ordenação. Um bom local é o método que retorna a lista para o dataTable:

    public ListDataModel getLista() {
        List relacao = dao.crud("from Person");
        ordenar(relacao);
        lista = new ListDataModel(relacao);
        return lista;
    }

Para finalizar, é necessário declarar os atributos e criar os métodos get e set para cada um deles. A seguir, um screenshot com a ordenação funcionando, e observamos que ela respeita a paginação, o que em muitas linguagens não funciona de maneira adequada.


terça-feira, 20 de abril de 2010

As extensões .iface, .jspx, .html

Às vezes parece um pouco complicado aprender a modificar o comportamento das páginas criadas com o framework IceFaces, para que as extensões sejam mostradas como .html.
O primeiro passo é modificar o arquivo web.xml, especificamente nesta seção:

    <servlet-mapping>
        <servlet-name>Persistent Faces Servlet</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

Dessa maneira, mesmo que as páginas tenham a extensão .xhtml, elas serão acessadas no navegador como .html.
Para facilitar os testes, podemos alterar mais esta seção do web.xml:

    <welcome-file-list>
        <welcome-file>pessoas.html</welcome-file>
    </welcome-file-list>

O nome do arquivo deve ser o da página inicial do sistema, que no meu caso é pessoas.html (no projeto continua como pessoas.xhtml). Se houver necessidade, podemos modificar uma propriedade do projeto para que já inicie com a página correta:


Criar a DataTable e colocar um paginador



Para terminar a integração do IceFaces com a persistência de dados, vamos criar uma página:

Novo->Outro->JavaServer Faces->Página JSF

Coloque o nome de pessoas e clique em finalizar. Baseado no modelo padrão (welcomeICEfaces), podemos criar o seguinte conteúdo:

01 <?xml version='1.0' encoding='UTF-8' ?>
02 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
03     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
04 <html xmlns="http://www.w3.org/1999/xhtml" 
05       xmlns:ui="http://java.sun.com/jsf/facelets" 
06       xmlns:f="http://java.sun.com/jsf/core" 
07       xmlns:h="http://java.sun.com/jsf/html" 
08       xmlns:ice="http://www.icesoft.com/icefaces/component">
09     <head>
10         <ice:outputStyle href="./xmlhttp/css/rime/rime.css" />
11     </head>
12     <body>
13         <ice:form>
14             <div align="center">
15                 <h1><ice:outputText value="Relação de pessoas" /></h1>
16                 <ice:dataTable value="#{controlador.lista}" 
17                                var="item" border="1">
18                     <ice:column>
19                         <f:facet name="header">
20                             <ice:outputText value="Nome"/>
21                         </f:facet>
22                         <ice:outputText value="#{item.name}" />
23                     </ice:column>
24                     <ice:column>
25                         <f:facet name="header">
26                             <ice:outputText value="Tratamento"/>
27                         </f:facet>
28                         <ice:outputText value="#{item.jobtitle}" />
29                     </ice:column>
30                     <ice:column>
31                         <f:facet name="header">
32                             <ice:outputText value="Atualizado em"/>
33                         </f:facet>
34                         <ice:outputText value="#{item.lastupdated}">
35                             <f:convertDateTime pattern="dd/MM/yyyy" />
36                         </ice:outputText>
37                     </ice:column>
38                 </ice:dataTable>
39             </div>
40         </ice:form>
41     </body>
42 </html>

Os pontos-chave desse arquivo estão nas seguintes linhas:

  • 10: um dos estilos pré-definidos que podem ser usados para decorar os componentes
  • 16: é onde indicamos qual atributo do bean gerenciado vai ser usado como fonte de dados para o DataTable.
  • 35: uma das maneiras de converter datas do banco de dados para a exibição em tela

O resultado pode ser visto na figura abaixo:


Para adicionar um dataPaginator, basta modificar o código da página dessa maneira:

01 <?xml version='1.0' encoding='UTF-8' ?>
02 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
03     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
04 <html xmlns="http://www.w3.org/1999/xhtml" 
05       xmlns:ui="http://java.sun.com/jsf/facelets"
06       xmlns:f="http://java.sun.com/jsf/core"
07       xmlns:h="http://java.sun.com/jsf/html"
08       xmlns:ice="http://www.icesoft.com/icefaces/component">
09     <head>
10         <ice:outputStyle href="./xmlhttp/css/rime/rime.css" />
11     </head>
12     <body>
13         <ice:form>
14             <div align="center">
15                 <h1><ice:outputText value="Relação de pessoas" /></h1>
16                 <ice:dataPaginator for="tabelaPessoas"
17                                    paginator="true"
18                                    fastStep="5"
19                                    paginatorMaxPages="3">
20                     <f:facet name="first">
21                         <ice:graphicImage
22                             url="./xmlhttp/css/xp/css-images/arrow-first.gif"
23                             style="border:none;"
24                             title="Primeira"/>
25                     </f:facet>
26                     <f:facet name="last">
27                         <ice:graphicImage
28                             url="./xmlhttp/css/xp/css-images/arrow-last.gif"
29                             style="border:none;"
30                             title="Última"/>
31                     </f:facet>
32                     <f:facet name="previous">
33                         <ice:graphicImage
34                             url="./xmlhttp/css/xp/css-images/arrow-previous.gif"
35                             style="border:none;"
36                             title="Anterior"/>
37                     </f:facet>
38                     <f:facet name="next">
39                         <ice:graphicImage
40                             url="./xmlhttp/css/xp/css-images/arrow-next.gif"
41                             style="border:none;"
42                             title="Próxima"/>
43                     </f:facet>
44                     <f:facet name="fastforward">
45                         <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-ff.gif"
46                                           style="border:none;"
47                                           title="Avanço rápido"/>
48                     </f:facet>
49                     <f:facet name="fastrewind">
50                         <ice:graphicImage url="./xmlhttp/css/xp/css-images/arrow-fr.gif"
51                                           style="border:none;"
52                                           title="Retrocesso rápido"/>
53                     </f:facet>
54                 </ice:dataPaginator>
55                 <ice:dataTable id="tabelaPessoas"
56                                value="#{controlador.lista}"
57                                var="item" border="1"
58                                rows="3">
59                     <ice:column>
60                         <f:facet name="header">
61                             <ice:outputText value="Nome"/>
62                         </f:facet>
63                         <ice:outputText value="#{item.name}" />
64                     </ice:column>
65                     <ice:column>
66                         <f:facet name="header">
67                             <ice:outputText value="Tratamento"/>
68                         </f:facet>
69                         <ice:outputText value="#{item.jobtitle}" />
70                     </ice:column>
71                     <ice:column>
72                         <f:facet name="header">
73                             <ice:outputText value="Atualizado em"/>
74                         </f:facet>
75                         <ice:outputText value="#{item.lastupdated}">
76                             <f:convertDateTime pattern="dd/MM/yyyy" />
77                         </ice:outputText>
78                     </ice:column>
79                 </ice:dataTable>
80             </div>
81         </ice:form>
82     </body>
83 </html>

A mudança mais importante está nas linhas 55 e 58. Elas indicam um id para a tabela, de maneira que possa ser referenciada no dataPaginator e a quantidade de linhas que serão exibidas. O resultado final é esse:


Voltar para DataTables básico

segunda-feira, 19 de abril de 2010

Criar um bean gerenciado que retorne uma lista com os dados

Nosso próximo passo é um Bean gerenciado que controle as requisições. É altamente recomendado que o projeto siga o padrão MVC - todas as requisições das páginas devem ser enviadas para este bean. Começamos com

Novo->Outro->JavaServer Faces->Bean gerenciado JSF

As configurações podem seguir o modelo a seguir, lembrando de colocar o arquivo no pacote controle, para manter o padrão:


As observações mais relevantes são a respeito do nome da classe e do nome que será usado nas páginas (ControladorBean e controlador, respectivamente). Essa diferenciação é apenas para facilitar a identificação.
Este Bean deve ter um método que retorne uma lista para montarmos o DataTable. Há várias maneiras de se fazer isto - vamos seguir este roteiro:

  1. instanciar a classe DAO;
  2. utilizar o método crud da classe DAO para recuperar os dados da tabela Person;
  3. criar uma instância de ListDataModel a partir dos dados recuperados.
O código final do Bean gerenciado ficará assim:
  
01 package controle; 02 03 import java.util.List; 04 import javax.faces.model.ListDataModel; 05 06 public class ControladorBean { 07 08 private ListDataModel lista; 09 private DAO dao; 10 11 public ControladorBean() { 12 dao = new DAO(); 13 } 14 15 public ListDataModel getLista() { 16 List relacao = dao.crud("from Person order by name"); 17 lista = new ListDataModel(relacao); 18 return lista; 19 } 20 21 }
O método getLista será referenciado na página onde será implementado o componente DataTable.

Voltar para DataTables básico

quinta-feira, 1 de abril de 2010

Criar um mapeamento no Hibernate


Existe uma maneira muito fácil para criar um mapeamento no Hibernate. Como as anotações já estão no modelo (ver em Criar uma classe de entidade a partir do banco de dados), basta que o arquivo hibernate.cfg.xml tenha uma referência à classe desejada - no nosso caso, a classe Person. Isto pode ser feito manualmente, inserindo a linha

<mapping class="modelo.Person" />
antes da declaração
</session-factory>
Também pode ser feito na aba Projeto do arquivo hibernate.cfg.xml, como mostra a figura. Basta abrir o nó Mapeamentos, clicar em Adicionar e preencher apenas o nome da classe.

É importante destacar que a simplicidade dessa etapa se dá porque as anotações a respeito dos atributos e de qual tabela do banco está sendo mapeada já foram feitas quando a classe Person foi criada.

Voltar para DataTables básico

Criar uma classe controladora (DAO)

(Confira minha nova série de posts: Spring + JPA + JTA)
 
A classe controladora DAO (Data Access Object é um padrão de projeto utilizado em engenharia de softwares orientados a objeto) vai se encarregar da ligação entre o Hibernate e as classes controladoras de cada CRUD da aplicação web. Ela pode ser construída particularmente para uma classe, mas utilizando polimorfismo podemos criar métodos que abstraem a classe para que seja totalmente reaproveitável.

No pacote controle, criaremos uma classe chamada DAO.java, com o código a seguir:

01 package controle;
02
03 import java.util.List;
04 import java.util.logging.Level;
05 import java.util.logging.Logger;
06 import org.hibernate.Session;
07
08 public class DAO {
09
10     private Session session;
11
12     public DAO() {
13     }
14
15     public boolean crud(Object o, String operacao) {
16         session = HibernateUtil.getSessionFactory().openSession();
17         session.beginTransaction();
18         switch (Integer.parseInt(operacao)) {
19             case 1: {
20                 session.save(o);
21                 break;
22             }
23             case 2: {
24                 session.delete(o);
25                 break;
26             }
27             case 3: {
28                 session.update(o);
29                 break;
30             }
31         }
32         session.getTransaction().commit();
33         session.close();
34         return true;
35     }
36
37     public Object crud(Object o, int id) {
38         Object objeto = null;
39         session = HibernateUtil.getSessionFactory().openSession();
40         Class classe = o.getClass();
41         try {
42             objeto = classe.newInstance();
43         } catch (InstantiationException ex) {
44             Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex);
45         } catch (IllegalAccessException ex) {
46             Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex);
47         }
48         objeto = session.get(o.getClass(), new Integer(id));
49         session.close();
50         return objeto;
51     }
52
53     public List crud(String sql) {
54         session = HibernateUtil.getSessionFactory().openSession();
55         session.beginTransaction();
56         List lista = session.createQuery(sql).list();
57         session.getTransaction().commit();
58         session.close();
59         return lista;
60     }
61 }
O código é bem simples, e não há muitas considerações a fazer. O método crud pode ser utilizado de três maneiras: uma para inserção, exclusão e atualização (linhas 15-35), outra para recuperar um objeto (linhas 37-51) e uma terceira que retorna uma lista de objetos (linhas 53-60).
Esta classe será instanciada no bean gerenciável que se encarregará da integração entre a interface visual e os dados.

Voltar para DataTables básico

quarta-feira, 31 de março de 2010

Criar uma classe de entidade a partir do banco de dados

Esta etapa também é bastante mecânica. O NetBeans se encarrega de criar uma classe a partir de uma tabela do banco de dados e colocar as anotações básicas. Em seguida, o desenvolvedor pode modificar o que achar necessário.
Vamos criar uma classe Person, baseada na tabela Person do banco de dados Travel, do Derby: basta utilizar Novo->Outro->Persistência->Classe de entidade a partir do banco de dados.
Em seguida, é só escolher a conexão e a tabela desejada. Na tela seguinte, o nome do pacote deve ser colocado (no nosso caso, modelo) e na última tela pode-se preencher o tipo de recuperação, o tipo de dados que será gerado (pode ser java.util.list) e pronto.
Se houverem tabelas associadas com chaves estrangeiras, o NetBeans se encarrega de criar as outras classes associadas, e colocar as anotações necessárias. Abaixo, a listagem final da classe Person.java - quanto tempo economizado!!


package modelo;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "PERSON", catalog = "", schema = "TRAVEL")
@NamedQueries({
@NamedQuery(name = "Person.findAll", query = "SELECT p FROM Person p"),
@NamedQuery(name = "Person.findByPersonid", query = "SELECT p FROM Person p WHERE p.personid = :personid"),
@NamedQuery(name = "Person.findByName", query = "SELECT p FROM Person p WHERE p.name = :name"),
@NamedQuery(name = "Person.findByJobtitle", query = "SELECT p FROM Person p WHERE p.jobtitle = :jobtitle"),
@NamedQuery(name = "Person.findByFrequentflyer", query = "SELECT p FROM Person p WHERE p.frequentflyer = :frequentflyer"),
@NamedQuery(name = "Person.findByLastupdated", query = "SELECT p FROM Person p WHERE p.lastupdated = :lastupdated")})
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "PERSONID")
private Integer personid;
@Column(name = "NAME")
private String name;
@Column(name = "JOBTITLE")
private String jobtitle;
@Column(name = "FREQUENTFLYER")
private Short frequentflyer;
@Column(name = "LASTUPDATED")
@Temporal(TemporalType.TIMESTAMP)
private Date lastupdated;

public Person() {
}

public Person(Integer personid) {
this.personid = personid;
}

public Integer getPersonid() {
return personid;
}

public void setPersonid(Integer personid) {
this.personid = personid;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getJobtitle() {
return jobtitle;
}

public void setJobtitle(String jobtitle) {
this.jobtitle = jobtitle;
}

public Short getFrequentflyer() {
return frequentflyer;
}

public void setFrequentflyer(Short frequentflyer) {
this.frequentflyer = frequentflyer;
}

public Date getLastupdated() {
return lastupdated;
}

public void setLastupdated(Date lastupdated) {
this.lastupdated = lastupdated;
}

@Override
public int hashCode() {
int hash = 0;
hash += (personid != null ? personid.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
if (!(object instanceof Person)) {
return false;
}
Person other = (Person) object;
if ((this.personid == null && other.personid != null) || (this.personid != null && !this.personid.equals(other.personid))) {
return false;
}
return true;
}

@Override
public String toString() {
return "modelo.Person[personid=" + personid + "]";
}

}

Voltar para DataTables básico