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