RSS

Arquivo da categoria: JSF

Paginacao Por Demanda com JSF – Parte 4

Complementando o post anterior, como solicitado em um comentário.
O rich:dataTable e o rich:datascroller deverão ficar da seguinte forma na pagina.

<rich:dataTable id=”TabelaUsuarios”  value=”#{usuarioBean.dataModel}”
binding=”#{usuarioBean.dataTable}” var=”usuario” rows=”10″>
<f:facet name=”header” />
<f:facet name=”footer”>
<rich:datascroller for=”TabelaUsuarios” stepControls=”hide” align=”center” />
</f:facet>
</rich:dataTable>

Caso seja necessário guardar o estado da tabela, poderá ser utilizado o tomahawk para isso. Basta fazer:

<t:saveState value=”#{usuarioBean.dataTable}”></t:saveState>

 
15 Comments

Publicado por em janeiro 22, 2009 em JSF

 

Paginação por Demanda com JSF – Parte 3

Note que os métodos são genéricos, mas que isso só funcionária se a query nunca tivesse nenhum tipo de restrição, ou seja, se você quisesse realizar o count apenas de determinadas informações, dessa maneira seria impossível e você teria que reescrever o método count sempre que precisasse restringir alguma informação.
Pensando nisso vamos abstrair mais ainda o Método count. Nele agora será necessário informar às restrições que deverão compor a query.
Vejamos como ficará o método count.

public Integer count(List<Criterion> restricoes){
Integer size = 0;
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());
if(restricoes != null){
for (Criterion criterion : restricoes) {
criteria.add(criterion);
}
}
criteria.setProjection(Projections.rowCount());
size = (Integer) criteria.uniqueResult();
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate().close();
}
return size;
}

Note que agora é solicitado uma Lista de restrições (Criterions) que serão utilizados pela criteria. Isso torna o método genérico a qualquer consulta, sendo necessário passar a restrição que desejar dentro de uma lista, da seguinte maneira.


List lista = new ArrayList();
lista.add(Restrictions.eq("usuario", usuario.GetUsuario()));

Dessa maneira é informado qual restrição quero para o método count.
Pensando dessa forma o método listByCriteriaDemanda deverá permitir restrições, ordenações e até mesmo projeções. Aqui entramos em um problema:
Teremos que prevenir o LIE (Lazy Initialization Exception). Se a sua entidade possuir uma listagem e está será utilizada na tela, a mesma deverá ser Lazy false ou você terá que realizar a prevenção do LIE. No meu caso teremos que inicializar essa dependência.

Mas como? Meu DAO é genérico, não tenho acesso aos atributos da classe e não sei se esta classe possui ou não listagens que poderão causar o LIE.
Vamos utilizar um recurso do Java. Vamos utilizar reflexão, ou seja, vamos descobrir se a classe possui ou não atributos que são listas e iremos inicializar esses atributos.

Vamos ver como ficaria o nosso método após a adição das restrições e inicialização de objetos de um determinado tipo por reflexão.

public List<T> listByCriteriaDemanda
(Integer startPage, Integer maxPage, List<Criterion> restricoes, List<Projection> projecoes, List<Order> ordenacao){

         List<T> list = new ArrayList<T>();

         try {

                   getHibernate().beginTransaction();

                   Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().
getClass());

if(restricoes !=null){

for(Criterion r: restricoes){

criteria.add(r);

}

}

if(projecoes != null){

for (Projection p : projecoes) {

criteria.setProjection(p);

}

}

if(ordenacao != null){

for(Order order: ordenacao){

criteria.addOrder(order);

}

}

criteria.setFirstResult(startPage);

criteria.setMaxResults(maxPage);

list = criteria.list();

for (T t : list) {

for (Method method : t.getClass().getMethods()) {

if(method.getName().indexOf(“get”) > -1){

Object obj = method.invoke(t);

if(obj instanceof java.util.Collection){

for (T lista: (List<T>)obj) {

Hibernate.initialize(lista);

}

}

}

}

}

getHibernate().commit();

} catch (Exception e) {

getHibernate().rollback();

e.printStackTrace();

} finally {

getHibernate.close();

}

return list;

}

Nosso método agora ficou bastante genérico, trouxemos as restrições, as projeções e as ordenações para dentro dele. Na nossa classe agora poderemos criar nossas restrições, projeções e ordenação e manter nosso DAO genérico, sem precisar implementar novamente as funcionalidades da classe.

O trecho abaixo é referente a parte de reflexão que iremos utilizar para descobrir se um atributo é uma coleção ou não, para podermos inicializar este atributo.

for (T t : list) {

 for (Method method : t.getClass().getMethods()) {

if(method.getName().indexOf(“get”) > -1){

Object obj = method.invoke(t);

if(obj instanceof java.util.Collection){

for (T lista: (List<T>)obj) {

Hibernate.initialize(lista);

}

}

}

}

}

Aqui nos fazemos uma iteração na lista criada pela criteria. Criamos um objeto do tipo Method, esse objeto é um Array e é carregado a partir do método

t.getClass.getMethods();

Esse método retorna um Methods[].

Feito isso nós testamos se no nome do método existe a palavra “get”, pois será necessário invocar esse get para sabermos qual o tipo de objeto será retornado.
O método indexOf retorna >-1 caso seja verdadeiro, ou seja, se a palavra get existir no nome do método então o retorno será verdadeiro.
Com isso nós invocamos o método através do method.invoke(t);
Como não sabemos qual o tipo de retorno então criamos um objeto do tipo Object que irá receber o retorno. Se esse objeto for filho de collection (List, Set, ArrayList, HashTree, etc) então iremos iterar sobre esse objeto e iremos inicializar item após item da listagem resultante. Por padrão, os relacionamentos OneToMany retornam um objeto do tipo PersistenceBag que é filho de Collection.

Feito isso sua classe está completamente inicializada e não haverá problemas com LazyInitialization e você terá uma paginação em demanda funcional.

Em breve estarei disponibilizando um projeto war contendo esse DAO, SuperDAO e as classes necessárias para realizar a paginação. Um exemplo funcional com acesso a uma base de dados.

 
1 Comment

Publicado por em dezembro 28, 2008 em Dicas, JSF

 

Paginação por Demanda com JSF – Parte 2

O get do DataModel terá que ter a seguinte aparência:

public DataModel getDataModel() {
  int totalListSize = dao.count();
  List<Usuário> pagedList = dao.listByCriteriaDemanda(getDataTable().getFirst(), getDataTable().getRows());
  dataModel = new PagedDataModel(pagedList, tatalListSize);
  return dataModel;
}     

Explicando o que cada um faz:

O primeiro carinha (totalListSize) é responsável por realizar uma consulta no banco que irá retornar a quantidade de linhas existentes para aquela consulta.
O segundo é responsável por realizar a consulta por demanda via hibernate, utilizando criteria para isso.
O terceiro seria a instancia do dataModel de acordo com a classe PagedDataModel que criamos.

Vamos criar os dois métodos, o primeiro para contar as linhas da tabela e o segundo para listar de acordo com a quantidade desejada. Sempre utilizando criteria.

No meu caso possuo uma classe chamada SuperDao que é genérica.
O método getEntidade retorna um objeto do tipo T e o método getHibernate me retorna o objeto Hibernate que está sendo utilizado.

public List<T> listByCriteriaDemanda(Integer startPage, Integer maxPage){

         List<T> list = new ArrayList<T>();

         try {

                  getHibernate().beginTransaction();

                  Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());

                  criteria.setFirstResult(startPage);

                  criteria.setMaxResults(maxPage);

                  list = criteria.list();

                  getHibernate().commit();

 

         } catch (Exception e) {

                  getHibernate().rollback();

                  e.printStackTrace();

          } finally {

                  getHibernate.close();

         }       

         return list;

}

public Integer count(){

         Integer size = 0;

         try {

                   getHibernate().beginTransaction();

                   Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());

                   criteria.setProjection(Projections.rowCount());

                   size = (Integer) criteria.uniqueResult();

                   getHibernate().commit();

         } catch (Exception e) {

                   getHibernate().rollback();

                   e.printStackTrace();

         } finally {

                   getHibernate().close();

         }

                   return size;

}

Parte 3

 
3 Comments

Publicado por em dezembro 28, 2008 em Dicas, JSF

 

Paginação por Demanda com JSF – Parte 1

Um dos grandes problemas de aplicações grande é o fato da Paginação em memória. Imaginem uma aplicação que trabalha com milhões de dados e que possui milhões de acesso simultâneo como o Ebay ou Mercado Livre, seria muito complicado lidar com milhões de informações na tela ou mesmo realizar uma paginação em memória.

PAGINAÇÃO EM DEMANDA

Há poucos dias comecei a pesquisar sobre esse tipo de paginação, devido aos fatos que expus acima. Nossa aplicação começou pequena, com poucas informações. Tudo corria muito bem até que a aplicação começou realmente a ser utilizada. No nosso caso esta aplicação serviria para controle de documentação acadêmica para um determinado evento que estava sendo organizado. Em principio nosso sistema reagiu bem, com a paginação em memória tudo ficou muito bonito, os filtros funcionavam e as informações eram exibidas com relativa velocidade.

Nossa aplicação era constituída de 4 módulos:
O primeiro era o módulo de cadastro de usuários, onde eles iriam informar dados básicos.
O segundo era um módulo de gerenciamento dos usuários.
O terceiro era um módulo de gerenciamento de atividades do usuário para o evento. (cadastro de material informativo, de resumos para apresentações, etc)
O quarto era um módulo administrativo para gerenciar as atividades de todos os usuários (aprovação cadastral, aprovação de trabalhos para apresentação, lotação de trabalhos, etc).

Os 3 primeiros módulos funcionaram corretamente, sendo que o 4 módulo foi o crucial para queremos “reescrever” a aplicação inteira. Este quarto modulo se tornou um peso enorme para a aplicação e para nossos servidores. Devido a grande massa de dados e a quantidade enorme de pessoas utilizando o sistema simultaneamente, nosso servidor sempre ficava indisponível, devido a paginação em memória.
Imaginem 100 pessoas acessando simultaneamente sua aplicação e solicitando 10 mil registros a cada clique do mouse? Isso daria em torno de 1 milhão de informações sendo carregadas simultaneamente na memória do computador.

Esse foi o motivo que me fez pensar em “paginação por demanda”. Já tinha ouvido falar deste tipo de paginação mas nunca tinha me deparado com uma situação real onde fosse necessário utilizá-la. Bom… deixando de lado esse “histórico” vamos ao que interessa: Paginação em Demanda.

Neste tipo de paginação que irei demonstrar é necessário utilizar-se de alguns componentes.
Vamos lá.

Primeiro é necessário criar uma classe que extenda o DataModel.
Essa classe terá que ter a seguinte aparência:

import java.util.List;
import javax.faces.model.DataModel;

public class PagedDataModel extends DataModel{

  private int rowIndex = -1;
  
  
private int totalNumRows;
  
  
private int pageSize;
  
  
private List list;
  
  
public PagedListDataModel() {
    
super();
  
}
  
  
public PagedListDataModel(List list, int totalNumRows) {
    
super();
    
setWrappedData(list);
    
this.totalNumRows = totalNumRows;
    
this.pageSize = list.size();
  
}
  
  
public boolean isRowAvailable() {
    
if(list == null)
      
return false;
    
    
int rowIndex = getRowIndex();
    
if(rowIndex >=&& rowIndex < list.size())
      
return true;
    
else
      
return false;
  
}

  public int getRowCount() {
    
return totalNumRows;
  
}

  public Object getRowData() {
    
if(list == null)
      
return null;
    
else if(!isRowAvailable())
      
throw new IllegalArgumentException();
    
else {
      
int dataIndex = getRowIndex();
      
return list.get(dataIndex);
    
}
  
}

  public int getRowIndex() {
    
return (rowIndex % pageSize);
  
}

  public void setRowIndex(int rowIndex) {
    
this.rowIndex = rowIndex;
  
}

  public Object getWrappedData() {
    
return list;
  
}
  
  
public void setWrappedData(Object list) {
    
this.list = (List) list;
  
}

}


Em alguns exemplos encontrados o construtor dessa classe possui 3 argumentos, a lista que será utilizada pelo dataModel, o número total de linhas que a tua consulta retornou e o número total de linhas que serão exibidos a cada página.

No meu exemplo o ultimo argumento foi retirado, pois por algum motivo a ultima página nunca era exibida. Digamos, minha paginação será de 10 em 10, possuo no banco 1903 registros, terei 190 paginas com 10 registros e uma página contendo 3 registros, essa página nunca era exibida. Testando percebi que se eu preenchesse os 10 valores com objetos vazios a página 191 era exibida. Para corrigir isso informo ao dataModel que o size da página será o size da lista.

Esse size é o “rows” que o dataTable possui, na paginação em memória setamos que seria 10 rows, na paginação em demanda esse valor se torna dinâmico.

Criado essa classe será necessário trazer alguns objetos para o teu Bean.
O primeiro seria um objeto do tipo DataModel e o segundo seria um HtmlDataTable, com seus get’s e set’s
O import do HtmlDataTable deverá ser igual ao componente que será utilizado na tela, se na tela você utilizar um h:dataTable então o import terá que ser o HtmlDataTable do JSF, se o componente for um rich:dataTable então o import terá que ser o HtmlDataTable do RichFaces.

 

Parte 2

 
1 Comment

Publicado por em dezembro 28, 2008 em Dicas, JSF

 

Paginação em Memória com JSF

A paginação em memória é realmente muito simples com o JSF e ajuda muito quando a página não contém muitas informações. Normalmente essa página é o espelho de um Managed Bean onde o mesmo possui um atributo referente a sua listagem.

Managed Bean UsuarioBean:

public class UsuarioBean {

      private Usuario usuario = new Usuario();

      private List<Usuario> listUsuario = new ArrayList<Usuario>();

      private UsuarioBO usuarioBO = new UsuarioBO();

      private String retorno

      public String list(){

            listUsuario = usuarioBO.findAll();

            retorno = "showListUsuario";

            return retorno;

      }     

      public Usuario getUsuario() {

            return usuario;

     

      public void setUsuario(Usuario usuario) {

            this.usuario = usuario;

     

      public List<Usuario> getListUsuario() {

            return listUsuario;

     

      public void setListUsuario(List<Usuario> listUsuario) {

            this.listUsuario = listUsuario;

     

      public UsuarioBO getUsuarioBO() {

            return usuarioBO;

     

      public void setUsuarioBO(UsuarioBO usuarioBO) {

            this.usuarioBO = usuarioBO;

      }

 }

 

 

 

faces-config.xml

<managed-bean>

   <description>Bean de Usuários do Sistema</description>

   <managed-bean-name>usuarioBean</managed-bean-name>

   <managed-bean-class>br.com.view.UsuarioBean</managed-bean-class>

   <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

 

 

Note que o nosso Managed Bean tem escopo de Sessão, sendo todas as informações guardadas na sessão do usuário no momento em que houver uma requisição deste Bean. Esse também é um problema para aplicações de grande porte, quanto mais Beans de sessão sua aplicação possuir mais memória seu servidor terá que alocar para manter as informações e se o tempo de sessão de cada usuário estiver muito longo poderá ocorrer um leak de memória e logo seu servidor ficará fora do ar.

A intenção é ter o máximo de Beans com escopo de requisição e manter informações necessárias através do a4j:keepAlive ou do t:saveState, o primeiro faz parte do pacote a4j encontrado no richFaces e o segundo faz parte do projeto Tomahawk da apache. Os dois conseguem manter um escopo maior que requisição e menor que sessão, o chamado escopo de “conversação” que o JBoss Seam possui. Isso faz com que algumas informações necessárias permaneçam vivas após a requisição ter sido encerrada.

PAGINAÇÃO EM MEMÓRIA

Para fazermos a paginação em memória utilizaremos o método list do Managed Bean para localizar todos os registros, colocar dentro de uma lista que está no escopo da classe e acessarmos na página diretamente em um DataTable ou DataGrid da seguinte forma:
 

<rich:dataTable id="tabelaUsuario" rows="10" var="c" value="#{usuarioBean.listUsuario}">

 <f:facet name="header">

 <rich:columnGroup>

     <rich:column>

          <h: outputText value="Código" />

     </rich:column>

     <rich:column>

          <h: outputText value="Usuário" />

     </rich:column>

</rich:columnGroup> 

</f:facet> 

<rich:column filterBy="#{c.id}" filterEvent="onkeyup">

     <f:facet name="header">

          <h: outputText value=" " />

     </f:facet>

     <h: outputText value="#{c.id}" />

</rich:column

<rich:column filterBy="#{c.usuario}" filterEvent="onkeyup">

     <f:facet name="header">

          <h: outputText value=" " />

     </f:facet>

     <h: outputText value="#{c.usuario}" />

</rich:column>

</rich:dataTable>

<rich:datascroller for="tabelaUsuario" maxPages="10" stepControls="hide" />

 

 

Note que o dataTable possui algumas informações especificas: A primeira seria a listagem que será utilizada (localizada no atributo value do dataTable), a segunda seria a variável que dará acesso aos atributos da lista, ou seja, o “item da vez”.

Destaquei em negrito alguns atributos interessantes.
Primeiro o filterBy do rich:column. Este atributo informa que, nesta coluna, existirá um filtro. Esse filtro é exibido como uma caixinha de texto logo abaixo do nome da coluna, para fazermos isso é necessário criar o objeto “facet” sendo o header da coluno e dentro desse facet é necessário que tenha um outputText onde o usuário poderá digitar o que desejar localizar.

Isto é um recurso muito interessante mas que funciona muito bem com a paginação em memória, pois todos os itens estão carregados na memória naquele instante e poderão ser “localizados” com o digitar do usuário. Explicando melhor: O usuário ao digitar uma palavra esse filtro irá começar a filtrar a listagem que se encontra na sessão do usuário naquele determinado momento, trazendo apenas as informações digitadas.

Na paginação por demanda isso se torna mais complicado, pois a listagem não está toda na memória, apenas parte dela. Para isso o usuário terá que criar um evento que irá buscar a informação a cada tecla que o usuário digitar, isso poderá acarretar em leak de memória novamente pois, dependendo da quantidade de informações dentro do BD, cada tecla digitada será realizado uma consulta no banco de dados.

Segundo está o nosso paginador padrão do RichFaces, o datascroller. Esse é o responsável por realizar a paginação em memória, nele informamos quantas páginas serão exibidas em seu paginador.

Esse seria um exemplo bem comum para a paginação em memória.

 
1 Comment

Publicado por em dezembro 27, 2008 em JSF

 

Tags:

Ciclo de Vida JSF

O JSF possui 6 fases no seu ciclo de vidas. Muitos programadores não compreendem o ciclo de vida do JSF, implentando um código se algumas funcionalidades.

As fases do ciclo de vida do JSF são:

  1. Restore View (recuperar a tela)
  2. Apply request values (Aplicar valores do request)
  3. Process Validation (Processo de Validação)
  4. Update model values (Atualização dos valores no Modelo)
  5. Invoke Application (Invocando a aplicação)
  6. Render response (retornar resposta)

Fase 1 – Restore View

Nesta fase o request vem atravês do controller do FacesContext. O Controller examina o request e extrai o ID da visão (tela, view) que é determinado pelo nome da página JSP.
O Framework do JSF utiliza esse ID para localizar os componentes para a tela atual. Se a visão ainda não existe ela será criada pelo framework, se ela já existir será apenas reutilizada pelo controller. A visão possui os componentes GUI’s (Graphical User Interface).
Esta fase possui 3 instâncias: nova visão, visão inicial e o retorno (postback) da visão. Cada instância dessa é tratada de forma diferente.
Na nova visão, o JSF constroi a visão para o Faces e vincula esta página aos handlers e validators. A visão é salva no FacesContext como um objeto. O FacesContext contêm todas as informações de como se deverá manipular este GUI para a requisição corrente. O FacesContext guarda as visões na propriedade ViewRoot o qual possui todos os componentes da visão atual.
Na visão inicial, o JSF cria uma visão em branco. Esta visão será populada de acordo com o evento solicitado pelo usuário. Da visão inicial o JSF avança diretamente para a fase de Render Response.
No caso do postback (o usuário retornou a página que acessou anteriormente), a visão já existe sendo necessário apenas ser restaurada.

Fase 2 – Apply Request values

O propósito desta fase é fazer com que cada componente recupere se estado corrente. O Componente deve primeiro ser criado ou recuperado a partir do FacesContext, seguido por seus valores. Os valores dos componentes são geralmente recuperados dos parametros de request, no entanto eles podem ser recuperados dos cabeçalhos ou cookies gerados.

Se um componente não está com seu atributo Immediate setado para True, o valor será apenas convertido. Se o campo for Inteiro, o valor será convertido para um inteiro. Se a conversão falhar será adicionado no FacesContext uma mensagem de erro que será exibida durante a fase de Render response.

Se um componente estiver com seu atributo immediate setado para true o valor será convertido para o tipo especifico do atributo e logo após será validado, o valor convertido será guardado no componente.

É nesta fase onde ocorrem as conversões de valores, ou seja, onde os Converter’s criado pelo usuário serão utilizados ou os conversores padrão do JSF.

Fase 3 – Process Validation

É nesta fase onde ocorrem as validações. Esta validação pode ser criada pelo desenvolvedor ou “obtida” diretamente do JSF. Os valores são validados de acordo com as regras de validação da aplicação. Se um valor estiver errado é gerado uma mensagem de erro e adicionada no FacesContext e o componente é marcado como inválido. Se algum componente estiver marcado como inválido o JSF avançará automaticamente para a fase de Render Response que irá exibir o erro de validação.
Se não houver nenhum erro de validação o JSF irá avançar para a fase de Update Model values.

Fase 4 – Update Model Values

Esta é a fase onde é atualizado o valor no lado do servidor, atualizando a propriedade dentro do seu Backing Bean. Somente as propriedades que estão sendo manipulados do componente é que serão atualizadas. Note que esta fase só irá acontecer após a fase de validação, o que lhe garante que os dados que estão indo para o seu Bean são válidos a nível de “tela” pois os mesmos poderão ser inválidos a nível de regras de negócio de sua aplicação.

Fase 5 – Invoke Application

Nesta fase o Controller do JSF invoca a aplicação para manipular o envio do form. O valor do componente sempre estará convertido, validado e aplicado aos objetos de modelo, então vc poderá utilizá-los para realizar suas regras de negócio.
Nesta fase vc especifica um “outcome” ou valor de saída que irá servir para continuar o fluxo lógico da aplicação. Você faz isso definindo uma “saída” quando houver sucesso no envio de um form e irá dar um retorno desta saída. Por exemplo, uma saída “sucesso” irá levar o usuário para a próxima página. Mas para essa navegação poder acontecer é necessário que seja criado um mapeamento dentro do arquivo faces-config.xml com este “outcome”.

Fase 6 – Render Response

Esta fase irá exibir a tela com todos os componentes em seu estado atual.

Retirado de: http://www-128.ibm.com/developerworks/library/j-jsf2/

 
5 Comments

Publicado por em dezembro 13, 2008 em JSF

 
 
Seguir

Obtenha todo post novo entregue na sua caixa de entrada.