Comportamento estranho com rich:dataTable

Fiz um pequeno projeto que é composto por uma única tabela chamada TipoTelefone, com duas colunas (cdTipTel=código do tipo de telefone [pk] e deTipTel=descrição do tipo de telefone), cujo domínio normal seria RES-Residencial, COM-Comercial, CEL-Celular ,FAX-Fax etc).

A tela permite a exibição em um rich:dataTable de todas a linhas da tabela. Através de uma coluna com um h:commandLink é permitida a escolha de um dos elementos da lista.

Coloquei também um h:inputText e um a4j:commandButton que permitem que se filtre as linhas da tabela. Caso o campo esteja vazio é exibida a lista completa.

Quando a lista é filtrada, embora o rich:dataTable esteja exibindo apenas os itens selecionados, quando se clica no link, ele seleciona outro elemento. Troquei para o h:dataTable e o comportamento foi o mesmo.

Ou seja, caso exista uma tabela com o domínio acima ordenada pela descrição e vc mandar pesquisar por COM, no dataTable aparecerá apenas uma linha (COM|Comercial). Se vc clicar no commandLink ele irá trazer o elemento CEL|Celular.

Consultei o HTML gerado e mesmo as linhas não filtradas estão sendo exibidas.

Se não for contra as regras, posso anexar o projeto eclipse (sem os jars é claro).

As tecnologias utilizadas são: JSF 1.2, Hibernate, RichFaces, Eclipse e no meu caso Oracle.

Obrigado antecipadamente.

Poderia, por gentileza, postar o código da sua página e de seu bean?

Abraços!

Código da Pagina

	<f:view>
		<f:loadBundle basename="i18n/TabelasBasicas" var="tabs"/>
		<rich:separator height="1" lineType="dotted" />
		<rich:spacer height="10" />
		<rich:panel id="panTipTel">
			<f:facet name="header">
				<h:outputText value="#{tabs['tabTipo-tipoTelefone-manutencao']}"/>
			</f:facet>
			<h:form id="formCadastro">
				<h:outputText value="#{tabs['tabTipo-codigo']}" />
				<h:inputText size="3" maxlength="3" id="cdTipTel" value="#{tipoTelefoneHandler.tipTel.cdTipTel}">
					<a4j:support event="onblur" ajaxSingle="true"/>
				</h:inputText>
				<h:outputText value="#{tabs['tabTipo-descricao']}" />
				<h:inputText size="30" maxlength="30" value="#{tipoTelefoneHandler.tipTel.deTipTel}" />
				<a4j:commandButton value="#{tabs['tabTipo-salvar']}" action="#{tipoTelefoneHandler.salva}" reRender="tblTiposTelefone,formCadastro" />
				<a4j:commandButton value="#{tabs['tabTipo-excluir']}" action="#{tipoTelefoneHandler.apaga}" reRender="tblTiposTelefone,formCadastro" />
				<a4j:commandButton value="#{tabs['tabTipo-pesquisar']}" action="#{tipoTelefoneHandler.pesquisa}" reRender="formCadastro,tblTiposTelefone"/>
				<rich:messages style="color: darkred"/>
			</h:form>
		</rich:panel>
		<rich:spacer height="10" />
		<h:form id="formTblTipTel">
			<rich:dataTable border="1" var="tt" value="#{tipoTelefoneHandler.lista}" 
					rendered="#{not empty tipoTelefoneHandler.lista}"
					rows="20" id="tblTiposTelefone">
				<h:column>
					<f:facet name="header">
						<h:outputText value="#{tabs['tabTipo-codigo']}" />
					</f:facet>
					<h:outputText value="#{tt.cdTipTel}" />
				</h:column>
				<h:column>
					<f:facet name="header">
						<h:outputText value="#{tabs['tabTipo-descricao']}" />
					</f:facet>
					<h:outputText value="#{tt.deTipTel}" />
				</h:column>
				<h:column>
					<f:facet name="header">
						<h:outputText value="#{tabs['tabTipo-acao']}" />
					</f:facet>
					<a4j:commandLink actionListener="#{tipoTelefoneHandler.escolheTipoTelefone}" reRender="formCadastro">
						<f:setPropertyActionListener value="#{tt}" target="#{tipoTelefoneHandler.tipTel}"/>
						<h:outputText value="#{tabs['tabTipo-escolher']}"/>
					</a4j:commandLink>
				</h:column>
				<f:facet name="footer">
					<rich:datascroller  />
				</f:facet>
			</rich:dataTable>
		</h:form>
	</f:view>

Código do Bean

package handler;

import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import modelo.TipoTelefone;

import org.hibernate.Session;

import dao.TipoTelefoneDao;

public class TipoTelefoneHandler {
	private TipoTelefoneDao dao;
	private List<TipoTelefone> lista;
	private TipoTelefone tipTel = new TipoTelefone();

	public TipoTelefoneHandler() {
		super();
	}

	public TipoTelefoneDao getDao() {
		return dao;
	}

	public void setDao(TipoTelefoneDao dao) {
		this.dao = dao;
	}

	public List<TipoTelefone> getLista() {
		System.out.println("**************************************************************************");
		System.out.println("**************************************************************************");
		System.out.println("**************************************************************************");
		System.out.println("**************************************************************************");
		System.out.println("===> Localizando tipo de telefone por (" + this.tipTel.getDeTipTel() + ")");
		//this.tiposTelefone.clear();
		if (this.tipTel.getDeTipTel() != null) {
			this.lista = this.dao.list(this.tipTel.getDeTipTel());
		} else {
			this.lista = this.dao.list();
		}
		System.out.println("===> QTDE DE TIPOS DE TELEFONE: " + this.lista.size());
		return this.lista;
	}

	public void setLista(List<TipoTelefone> lista) {
		this.lista = lista;
	}

	public TipoTelefone getTipTel() {
		return tipTel;
	}

	public void setTipTel(TipoTelefone tipTel) {
		this.tipTel = tipTel;
	}

	
	
	public void setSessao(Session s) {
		this.dao = new TipoTelefoneDao(s);
	}
	
	public String salva() {
		System.out.println("Adicionando: " + tipTel.getDeTipTel());
		this.dao.merge(this.tipTel);		this.tipTel = new TipoTelefone();
		return "";
	}

	public String pesquisa() {
		System.out.println("Pesquisando: " + this.tipTel.getDeTipTel());
		this.getLista();
		return "";
	}

	public String apaga() {
		System.out.println("Eliminando: " + this.tipTel.getCdTipTel() + " " + this.tipTel.getDeTipTel());
		if (this.tipTel.getCdTipTel().length() > 0) {
			try {
				this.dao.delete(this.tipTel);
				this.tipTel = new TipoTelefone();
			} catch (Exception e) {
				FacesMessage msg = new FacesMessage(e.getMessage());
				FacesContext.getCurrentInstance().addMessage("erro", msg);
			}
		} else {
			FacesMessage msg = new FacesMessage("Preencha o \"código\"");
			FacesContext.getCurrentInstance().addMessage("erro", msg);
		}
		return "sucesso";
	}

	public void escolheTipoTelefone(ActionEvent e) {
		System.out.println("Localizando tipo de telefone:" + this.tipTel.getDeTipTel());
	}
}

Métodos List da Classe DAO

	@SuppressWarnings("unchecked")
	public List<TipoTelefone> list(String deTipTel) {
		Criteria c = this.sessao.createCriteria(TipoTelefone.class);
		c.add(Restrictions.ilike("deTipTel", deTipTel.toLowerCase(), MatchMode.ANYWHERE));
		return c.list();
	}

	public List<TipoTelefone> list() {
		return dao.list();
	}

Olhei o seu código e acredito que o problema esteja relacionado ao modo com o qual o JSF processa as fases de seu ciclo.

Quando você faz chamadas ajax, o bean não tem estado, logo a variável lista está vazia qdo o bean é acionado pelo ajax.

A lista é preenchida pelo this.dao.list(); pq, no primeiro momento, o escolheTipoTelefone(ActionEvent e) não é acionado e this.tipTel está null. Num segundo momento, quando o evento é processado e tipTel é preenchido, lista está contendo o valor de 5 registros, não mais o que tinha sido originalmente pesquisado.

Se você faz uma pesquisa e retorna 2 resultados, qdo vc, por ex, escolhe o segundo resultado, o dataTable pede o índice 2 da coleção para atribuir seu valor ao objeto tipTel. O bean então remonta a lista e, no seu código, irá recriar a lista original inteira, pois tipTel ainda é null.

Qdo o bean recebe a informação de q precisa passar pro dataTable o valor do índice 2, ele passa o q estiver na lista, que será o segundo valor da lista original e não da pesquisada.

Não sei se ficou claro.

Para verificar se é isso mesmo, aconselho temporariamente usar o bean como session e só tentar repreencher a lista qdo ela estiver nula. ASsim, qqer resultado dentro dela será preservado.

BigJoe,

Entendi o seu raciocínio. Eu considerei que o problema era nas fases do JSF, então, considerando o escopo de request, no meu entendimento o fluxo deveria ser assim:

  1. Primeiro request: A variável deTipTel está vazia, logo é montada a lista inteira.
    A visão é montada, o bean é construído e as suas propriedades são preenchidas com os valores de request correspondentes (vazios).
    Na fase de Invocar Aplicação, como o texto de pesquisa não está preenchido, a propriedade lista é preenchida com todas as linhas da tabela.
  2. Segundo request: Preencho a variável deTipTel e pressiono o botão PESQUISAR.
    A visão é montada, o bean é construído (o bean anterior já morreu, pois é outro request) e as suas propriedades são preenchidas com os valores de request correspondentes (a propriedade de texto da pesquisa do bean agora está preenchida e a lista continua vazia).
    Na fase de Invocar Aplicação, como o texto de pesquisa está preenchido, a propriedade lista é preenchida com as linhas da tabela que atendem ao critério (ilike).
  3. Terceiro request (cliquei no commandLink).
    A visão é montada, o bean é construído (o bean anterior já morreu, pois é outro request) e as suas propriedades são preenchidas com os valores de request correspondentes (a propriedade de texto da pesquisa do bean agora está preenchida e a lista continua vazia).
    Na fase de Invocar Aplicação, como o texto de pesquisa está preenchido, a propriedade lista é preenchida com as linhas da tabela que atendem ao critério (ilike), logo o índice deveria apontar para o mesmo elemento do segundo request.

Troquei o escopo para session.

	<managed-bean>
		<managed-bean-name>tipoTelefoneHandler</managed-bean-name>
		<managed-bean-class>handler.TipoTelefoneHandler</managed-bean-class>
		<managed-bean-scope>session</managed-bean-scope>
		<managed-property>
			<property-name>sessao</property-name>
			<value>#{sessionScope.sessao}</value>
		</managed-property>
	</managed-bean>

Contudo o método List do DAO parou de funcionar.

Alterei o método para ver o que estava acontecendo e observei que é quando ele executa a linha “lista = session.createCriteria(persistentClass).list();”

	@SuppressWarnings("unchecked")
	public List<T> list() {
		logger.info("Listando todos");
		List<T> lista;
		lista = session.createCriteria(persistentClass).list();
		System.out.println("DAO: lista qtde elementos:" + lista.size());
		return lista;
	}

Você consegue me ajudar a resolver o problema da exceção acima para que eu consiga completar o teste com escopo de sessão?

Existem uma série de posts falando desse comportamento estranho. Muitos resolvem estendendo o comportamento do commandLink (http://www.theserverside.com/news/thread.tss?thread_id=44186, http://typo.ars-subtilior.com/articles/2007/02/07/jsf-datatable-and-commandlink). Eu porém com o meu Java de iniciante não consegui ir muito longe.

Tentei trocar o escopo do meu bean de request para session e começou dar um erro que eu não consegui resolver.

O que eu estou fazendo é uma coisa muito simples: Filtrar elementos de uma lista e navegar a partir da lista filtrada.

Por hora, me ajudaria muito se alguém pudesse me ajudar na questão da troca do escopo de request para session.

	<managed-bean>
		<managed-bean-name>tipoTelefoneHandler</managed-bean-name>
		<managed-bean-class>handler.TipoTelefoneHandler</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
		<managed-property>
			<property-name>sessao</property-name>
			<value>#{requestScope.sessao}</value>
		</managed-property>
	</managed-bean>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://richfaces.org/a4j" prefix="a4j" %>
<%@ taglib uri="http://richfaces.org/rich" prefix="rich" %>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>eCob - Tabelas Básicas</title>
</head>
<body>
	<f:view>
		<f:loadBundle basename="i18n/TabelasBasicas" var="tabs"/>
		<rich:separator height="1" lineType="dotted" />
		<rich:spacer height="10" />
		<h:form id="formCadastro">
			<rich:panel id="panTipTel">
				<f:facet name="header">
					<h:outputText value="#{tabs['tabTipo-tipoTelefone-manutencao']}"/>
				</f:facet>
				<br/><h:outputText value="#{tabs['tabTipo-codigo']}" />
				<h:inputText size="3" maxlength="3" id="cdTipTel" value="#{tipoTelefoneHandler.tipTel.cdTipTel}">
					<a4j:support event="onblur" ajaxSingle="true"/>
				</h:inputText>
				<h:outputText value="#{tabs['tabTipo-descricao']}" />
				<h:inputText size="30" maxlength="30" value="#{tipoTelefoneHandler.tipTel.deTipTel}" />
				<a4j:commandButton value="#{tabs['tabTipo-salvar']}" action="#{tipoTelefoneHandler.salva}"/>
				<a4j:commandButton value="#{tabs['tabTipo-excluir']}" action="#{tipoTelefoneHandler.apaga}"/>
				<a4j:commandButton value="#{tabs['tabTipo-pesquisar']}" action="#{tipoTelefoneHandler.pesquisa}"/>
				<rich:messages style="color: darkred"/>
			</rich:panel>
			<rich:spacer height="10" />
			<h:dataTable border="1" var="tt" value="#{tipoTelefoneHandler.lista}" 
				rows="20" id="tblTiposTelefone">
				<h:column>
					<f:facet name="header">
						<h:outputText value="#{tabs['tabTipo-codigo']}" />
					</f:facet>
					<h:outputText value="#{tt.cdTipTel}" />
				</h:column>
				<h:column>
					<f:facet name="header">
						<h:outputText value="#{tabs['tabTipo-descricao']}" />
					</f:facet>
					<h:outputText value="#{tt.deTipTel}" />
				</h:column>
				<h:column>
					<f:facet name="header">
						<h:outputText value="#{tabs['tabTipo-acao']}" />
					</f:facet>
					<h:commandLink actionListener="#{tipoTelefoneHandler.escolheTipoTelefone}">
						<h:outputText value="#{tabs['tabTipo-escolher']}"/>
						<f:setPropertyActionListener value="#{tt}" target="#{tipoTelefoneHandler.tipTel}"/>
					</h:commandLink>
				</h:column>
			</h:dataTable>
		</h:form>
	</f:view>
</body>
</html>
package handler;

import java.util.ArrayList;
import java.util.List;

import javax.faces.event.ActionEvent;

import modelo.TipoTelefone;

import org.hibernate.Session;

import dao.TipoTelefoneDao;

public class TipoTelefoneHandler {
	private TipoTelefoneDao dao;
	private TipoTelefone tipTel = new TipoTelefone();
	private List<TipoTelefone> lista = new ArrayList<TipoTelefone>();
	
	public TipoTelefoneHandler() {
		super();
	}

	public TipoTelefoneDao getDao() {
		return dao;
	}

	public void setDao(TipoTelefoneDao dao) {
		this.dao = dao;
	}

	public List<TipoTelefone> getLista() {
		System.out.println("**************************************************************************");
		System.out.println("===> Localizando tipo de telefone por (" + this.tipTel.getDeTipTel() + ")");
		if (this.tipTel.getDeTipTel() != null) {
			this.lista = this.dao.list(this.tipTel.getDeTipTel());
		} else {
			this.lista = this.dao.list();
		}
		System.out.println("===> QTDE DE TIPOS DE TELEFONE: " + lista.size());
		return this.lista;
	}

	public void setLista(List<TipoTelefone> lista) {
		this.lista = lista;
	}

	public TipoTelefone getTipTel() {
		return tipTel;
	}

	public void setTipTel(TipoTelefone tipTel) {
		this.tipTel = tipTel;
	}

	public void setSessao(Session s) {
		this.dao = new TipoTelefoneDao(s);
	}
	
	public String salva() {
		System.out.println("Adicionando: " + tipTel.getDeTipTel());
		this.dao.merge(this.tipTel);		this.tipTel = new TipoTelefone();
		return "";
	}

	public String pesquisa() {
		System.out.println("Pesquisando: " + this.tipTel.getDeTipTel());
		this.getLista();
		return "tipoTelefone";
	}

	public String apaga() {
		System.out.println("Eliminando: " + this.tipTel.getCdTipTel() + " " + this.tipTel.getDeTipTel());
		if (this.tipTel.getCdTipTel().length() > 0) {
			this.dao.delete(this.tipTel);
			this.tipTel = new TipoTelefone();
		}
		return "sucesso";
	}

	public void escolheTipoTelefone(ActionEvent e) {
		System.out.println("==============================================================================");
		System.out.println("Localizando tipo de telefone:" + this.tipTel.getDeTipTel());
	}

	public String escolhe() {
		System.out.println("------------------------------------------------------------------------------");
		System.out.println("Localizando tipo de telefone:" + this.tipTel.getDeTipTel());
		return "tipoTelefone";
	}
}

Quando eu troco o escopo para session, recebo um java.lang.NullPointerException na hora que estou obtendo a lista no banco de dados, o que não acontece quando o escopo é de request.

Orlando, vou olhar para você o código, mas provavelmente só na noite de sexta e te respondo. Espero que não fique tarde demais para você, mas estou em semana de provas.

Obrigado pela ajuda.
Claro que não será tarde.
Sds,