Richfaces com paginação sob demanda (true pagination)

Colegas,

Estou com dúvidas (na verdade perdido mesmo) em como implementar true pagination, que traz os dados paginados do banco e não pegar tudo do banco e paginar na memória.

Pesquisando, encontrei a classe abaixo, porém não sei o que vai no Managed Bean e no xhml (Uso richfaces e hibernate)

Um exemplo seria bem-vindo.

Muito obrigado,

Marques

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

public class PagedDataModel extends DataModel {

	private int rowIndex = -1;
	private int totalNumRows;
	private int pageSize;
	@SuppressWarnings("unchecked")
	private List list;

	public PagedDataModel() {
		super();
	}

	@SuppressWarnings("unchecked")
	public PagedDataModel(List list, int totalNumRows) {
		super();
		setWrappedData(list);
		this.totalNumRows = totalNumRows;
		this.pageSize = list.size();
	}

	@SuppressWarnings("unchecked")
	public PagedDataModel(List list, int totalNumRows, int pageSize) {
		super();
		setWrappedData(list);
		this.totalNumRows = totalNumRows;
		this.pageSize = pageSize;
	}

	@Override
	public boolean isRowAvailable() {
		if (list == null)
			return false;

		int rowIndex = getRowIndex();
		if (rowIndex >= 0 && rowIndex < list.size())
			return true;
		else
			return false;
	}

	@Override
	public int getRowCount() {
		return totalNumRows;
	}

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

	@Override
	public int getRowIndex() {
		try {
			return (rowIndex % pageSize);
		} catch (ArithmeticException e) {
			return 0;
		}
	}

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

	@Override
	public Object getWrappedData() {
		return list;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void setWrappedData(Object list) {
		this.list = (List) list;
	}
}

blz meu amigo, vou tentar te ajudar, bom esta classe ai vai te retornar um datamodel, para vc fazer um binding em seu datatable.
1 - pode ver que no construtor vc vai passar a lista, o numero total de linhas e da paginação e a quantidade de linhas por pagina;
2 - em seu managedbean vc tem que ter um atributo do tipo datamodel tipo:


public DataModel getTableModel(){
        
        int totalSize = 0;
        int first = 0;
        int rows = 6;

        //aqui é só para garantir que o table não fique nullo, ha vc precisa de um atributo table do tipo UIData
        if(table != null){
            first = table.getFirst();
            rows = table.getRows();
        }
        try {
            //aqui eu consulto a lista de usuarios passando o numero do primeiro resultado, e o numero de linhas para mostrar
            usuarios = getFachada().consultaTodosUsuarios(first, rows);

            //aqui eu consulto o numero total de usuarios cadastrados no banco
            totalSize = getFachada().consultarTotalTodosUsuarios();
        } catch (ServicoException ex) {
            ex.printStackTrace();
        }

        // aqui eu retorno o datamodel com a lista, o numero total e o numero por página
        return super.getPaginationDataModel( usuarios, totalSize, rows); // este cara aqui faz a mesma coisa que sua classe faz
    }

lá no seu arquivo gui, vc vai fazer:


<rich:dataTable
                        id="tabelaPaginada"
                        styleClass="styleTable" rowClasses="odd-row,even-row"

                        cellspacing="0"  value="#{actionUsuario.tableModel}" <!-- aqui o seu atributo do tipo DataModel  -->
                        var="row"

                        binding="#{actionUsuario.table}" <!-- este aqui é o binding do atributo UIData do seu managedbean -->

                        rows="6"> <!-- este cara aqui é o numero de linhas para mostrar nesta tabela -->

.....

e vc pode usar o proprio componente do table para paginar e não vai ficar na memoria pq vc já esta tratando com a classe que vc criou ai


<f:facet name="footer">
                  <rich:datascroller for="tabelaPaginada" maxPages="10" /> <!-- um campo é o id do datatable e o outro é o número de paginas da paginação, vai aparecer de 10 em 10 números -->
</f:facet>

tenta ai, qualquer coisa pergunta blz.

Meu caro jbcordeiro,

Vc poderia me passar os métodos abaixo?
getFachada().consultaTodosUsuarios(first, rows);
totalSize = getFachada().consultarTotalTodosUsuarios();

Muito obrigado,

Marques

vc utiliza hibernate?

nele tem o

.setFirstResult(index) // este vai pegar apartir de qual index
.setMaxResults(maxResults); // este pega a quantidade de linhas apartir do index

com estes métodos sua consulta faz a paginação de dados direto no banco, trazendo apenas a quantidade necessária.

sim

lá do seu Dao(ou qualquer outro padrão que vc utilize), mostra ai sua consulta para mostrar no datatable

e quando há 2 lista(grid) na mesma página?!?
as 2 grids não aparecem ao mesmo tempo,
são apresentadas em diferentes status!

quando a 2ª grid esta sendo apresentada e a mesma possui paginação,
ao clicar para mudar de página, a ação gerada no backing esta sendo
como se fosse para a 1ª grid que no momento nem esta sendo exibida.

Ok… 10!

Bom dia galera… Estou tentando usar esse exemplo com SEAM mas estou com problemas:

PagedDataModel:

public class PagedDataModel extends DataModel {

	private int rowIndex = -1;
	private int totalNumRows;
	private int pageSize;
	
	private List<?> list;

	public PagedDataModel() {
		super();
	}

	public PagedDataModel(List<?> list, int totalNumRows) {
		super();
		setWrappedData(list);
		this.totalNumRows = totalNumRows;
		this.pageSize = list.size();
	}

	public PagedDataModel(List<?> list, int totalNumRows, int pageSize) {
		super();
		setWrappedData(list);
		this.totalNumRows = totalNumRows;
		this.pageSize = pageSize;
	}

	@Override
	public boolean isRowAvailable() {
		if (list == null)
			return false;

		int rowIndex = getRowIndex();
		if (rowIndex >= 0 && rowIndex < list.size())
			return true;
		else
			return false;
	}

	@Override
	public int getRowCount() {
		return totalNumRows;
	}

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

	@Override
	public int getRowIndex() {
		try {
			return (rowIndex % pageSize);
		} catch (ArithmeticException e) {
			return 0;
		}
	}

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

	@Override
	public Object getWrappedData() {
		return list;
	}

	@Override
	public void setWrappedData(Object list) {
		this.list = (List<?>) list;
	}
}

ManagedBean:

@Name("usuarioBean")
@Scope(ScopeType.PAGE)
public class UsuarioBean extends BaseBean{

	private static final long serialVersionUID = -8176266937869789953L;
	
	List<Usuario> usuarios;
	
	@In(create = true, required=false)
	private UsuarioSessionLocal usuarioSessionLocal;
	
	@In(create = true)
	private PagedDataModel pagedDataModel;

	@In(create = true)
	@Out
	private UIData table;
	
	private javax.faces.model.DataModel tableModel;
	
	public javax.faces.model.DataModel getTableModel(){
        
        int totalSize = 0;
        int first = 0;
        int rows = ConstantesUtil.MAX_RESULT;
        
        //aqui é só para garantir que o table não fique nulo, ha vc precisa de um atributo table do tipo UIData
        if(table != null){
            first = table.getFirst();
            rows = table.getRows();
        }
        try {
            //aqui eu consulto a lista de usuarios passando o numero do primeiro resultado, e o numero de linhas para mostrar
            usuarios = usuarioSessionLocal.getUsuarios(first, rows);

            //aqui eu consulto o numero total de usuarios cadastrados no banco
            totalSize = usuarioSessionLocal.getTotalUsuarios();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // aqui eu retorno o datamodel com a lista, o numero total e o numero por página
        pagedDataModel = new PagedDataModel(usuarios, totalSize, rows);
        
        return (javax.faces.model.DataModel) pagedDataModel;
        
    }

}

.xhtml

<rich:dataTable width="940px" id="tabelaPaginada"
					value="#{tableModel}" rows="20" var="usuario"
					binding="#{table}">

					<rich:column>
						<f:facet name="header">
							<h:outputLabel value="LOGIN" />
						</f:facet>
						<h:outputText value="#{usuario.login}" />
					</rich:column>
					<rich:column>
						<f:facet name="header">
							<h:outputLabel value="SENHA" />
						</f:facet>
						<h:outputText value="#{usuario.senha}" />
					</rich:column>

O problema que eu estou tendo inicialmente é no VALUE do datatable, ele não chama o método do meu bean que vai fazer a paginação (getTableModel). Se no value do dataTable eu passar #{usuarioBean.tableModel} ele dá o seguinte erro:

11:30:46,713 ERROR [viewhandler] Error Rendering View[/pages/acesso/usuario/usuarioMain.xhtml]
javax.faces.FacesException: javax.el.ELException: /pages/acesso/usuario/usuarioMain.xhtml @29,25 value="#{usuarioBean.tableModel}": Error reading 'tableModel' on type br.com.bb.acesso.bean.UsuarioBean_$$_javassist_seam_2
	at javax.faces.component.UIData.getValue(UIData.java:612)

Se eu deixo como está, a tela abre, mas não chama o método que vai fazer a paginação.

Qualquer ajuda vai ser bem vinda…

Agradecendo desde já,

Resolvi em partes o problema. Ele não entrava no meu método pq eu estava usando a anotation @In do Seam no tableModel.

tirando o @In ele consegue entrar no método assim:

<rich:dataTable width="940px" id="tabelaPaginada"
					value="#{usuarioBean.tableModel}" rows="1" var="usuario"
					binding="#{table}">

					<rich:column>
						<f:facet name="header">
							<h:outputLabel value="LOGIN" />
						</f:facet>
						<h:outputText value="#{usuario.login}" />
					</rich:column>
					<rich:column>
						<f:facet name="header">
							<h:outputLabel value="SENHA" />
						</f:facet>
						<h:outputText value="#{usuario.senha}" />
					</rich:column>

				</rich:dataTable>

Ai com isso ta fazendo a paginação certinha.
Com um porém, rss. Qdo eu clico pra ir pra segunda paginação, ele está entrando 3 vezes no getTableModel(), fazendo a mesma consulta 3 vezes.

Alguém tem idéia do pq ??

Olá Cristiano,

[quote]Qdo eu clico pra ir pra segunda paginação, ele está entrando 3 vezes no getTableModel(), fazendo a mesma consulta 3 vezes.

Alguém tem idéia do pq ??[/quote]

Já vi essa dúvida em vários posts aqui mesmo no GUJ (e nao lembro de ter visto a resposta).

Sugiro a leitura do Capítulo " 6.7 Factory and manager components " do livro Seam In Action.

Pelo que compreendi: durante o ciclo de vida do JSF, o componente ui pode ser acessado/visitado várias vezes. É o comportamento normal do JSF.
A sugestão é utilizar o @Factory que executará a consulta na primeira vez e armazenará o valor na variável de contexto. Nas solicitações seguintes é recuperado o valor que foi armazenado.
Acredito que será necessário estudar sobre o @Factory, @Unwrap, @Observer para que você saiba como utilizá-los de uma forma onde a consulta no banco seja executada nos momentos apropriados.

Olá pessoal,

O método table.getFirst() não funcionou para mim, ele sempre retorna ZERO.

Tive que fazer dessa forma para funcionar: first = (scroller.getPage() * rows) - rows;

Alguém sabe o que pode ter ocorrido para não funcionar?

Utilizo: JSF 2.1 e RichFaces 4.1.

Olá, pessoal.
Achei muito interessante o modelo de vocês, mas estou tendo problemas com a view, onde às vezes, tenho simplesmente a tela sem o layout do richfaces, sem tema. nada…
A imagem diz o que acontece mais do que se explicasse, muito embora, enquanto executo obtenho todos os dados do modo como preciso, porém após clicar duas vezes sobre o botão que chama o dataModel a rich:dataTable fica sem layout algum.
Podem me ajudar?
Este é o código da página:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:a4j="http://richfaces.org/a4j"
	xmlns:rich="http://richfaces.org/rich"&gt;

&lt;ui:composition template="/template/template.xhtml"&gt;
	&lt;ui:define name="corpo"&gt;
		&lt;f:view&gt;
			&lt;rich:panel bodyClass="paineis"&gt;
				&lt;f:facet name="header"&gt;
					&lt;h:outputText value="Consulta de Atendimentos"&gt;&lt;/h:outputText&gt;
				&lt;/f:facet&gt;

				&lt;h:panelGroup&gt;
					&lt;rich:dataTable value="#{atendimentoBean.dataModel}" 
						binding="#{atendimentoBean.uiDataTable}" var="_atendimentos"
						rows="2" id="atendimentosTable" styleClass="tabelas"
						rowKeyVar="rowIndex" rowClasses="even-row"
						noDataLabel="Não existem atendimentos cadastradas."&gt;
						&lt;f:facet name="header"&gt;
							&lt;rich:columnGroup&gt;
								&lt;rich:column colspan="7"&gt;
									&lt;h:outputText id="atendimento" value="Atendimentos" /&gt;
								&lt;/rich:column&gt;

								&lt;rich:column breakRowBefore="true"&gt;
									&lt;h:outputText id="data" value="Data da coleta" /&gt;
								&lt;/rich:column&gt;

								&lt;rich:column colspan="4"&gt;
									&lt;h:outputText id="ordem" value="Num. de ordem" /&gt;
								&lt;/rich:column&gt;

								&lt;rich:column&gt;
									&lt;h:outputText id="acoes" value="Ações" /&gt;
								&lt;/rich:column&gt;
							&lt;/rich:columnGroup&gt;
						&lt;/f:facet&gt;

						&lt;rich:column&gt;
							&lt;rich:collapsibleSubTableToggler for="sbtblrec" /&gt;
							&lt;h:outputText id="dataColeta" value="#{_atendimentos.dataColeta}"&gt;
								&lt;f:convertDateTime pattern="dd/MM/yyyy" /&gt;
							&lt;/h:outputText&gt;
						&lt;/rich:column&gt;

						&lt;rich:column colspan="4"&gt;
							&lt;h:outputText id="numOrdem" value="#{_atendimentos.numOrdem}" /&gt;
						&lt;/rich:column&gt;

						&lt;rich:column style="text-align:center;"&gt;
							&lt;a4j:commandLink action="editarAtendimento"&gt;
								&lt;h:graphicImage library="images" name="alterar.png"
									title="Editar" styleClass="imagemLink" width="20" height="20"
									style="border:0" /&gt;
								&lt;f:setPropertyActionListener value="#{_atendimentos}"
									target="#{atendimentoBean.atendimentoEdicao}"&gt;
								&lt;/f:setPropertyActionListener&gt;
							&lt;/a4j:commandLink&gt;

							&lt;h:commandLink id="linkExcluir" title="Excluir"
								actionListener="#{atendimentoBean.editar}"&gt;
								&lt;rich:componentControl target="modalPanelExclusaoAtendimento"
									operation="show"&gt;
								&lt;/rich:componentControl&gt;
								&lt;h:graphicImage library="images" name="excluir.png"
									title="Excluir" styleClass="imagemLink" width="20" height="20"
									style="border:0" /&gt;
							&lt;/h:commandLink&gt;

							&lt;rich:popupPanel id="modalPanelExclusaoAtendimento"
								resizeable="true" modal="true" width="300" height="110"&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="conf" value="Confirmação de exclusão" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText value="Deseja realmente excluir o item?" /&gt;
								&lt;h:panelGrid columns="2"
									style="margin-left:70px; margin-top:10px;"&gt;
									&lt;h:panelGroup&gt;
										&lt;a4j:commandButton value="Cancelar"
											onclick="#{rich:component('modalPanelExclusaoAtendimento')}.hide(); return false;"&gt;
										&lt;/a4j:commandButton&gt;

										&lt;a4j:commandButton value="Excluir" render="atendimentosTable"
											execute="@this" action="#{atendimentoBean.excluir}"
											immediate="true"
											onclick="#{rich:component('modalPanelExclusaoAtendimento')}.hide()"&gt;
											&lt;f:setPropertyActionListener value="#{_atendimentos}"
												target="#{atendimentoBean.atendimentoEdicao}"&gt;
											&lt;/f:setPropertyActionListener&gt;
										&lt;/a4j:commandButton&gt;
									&lt;/h:panelGroup&gt;
								&lt;/h:panelGrid&gt;
							&lt;/rich:popupPanel&gt;
						&lt;/rich:column&gt;

						&lt;rich:collapsibleSubTable value="#{_atendimentos.receptores}"
							var="receptor" expandMode="client" id="sbtblrec"&gt;
							&lt;rich:column&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="nomeRec" value="Nome do receptor" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="nomeReceptor" value="#{receptor.nomeReceptor}" /&gt;
							&lt;/rich:column&gt;

							&lt;rich:column&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="numBolsa" value="Num. bolsa receptor" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="numeroDaBolsa" value="#{receptor.numBolsa}" /&gt;
							&lt;/rich:column&gt;

							&lt;rich:column&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="tipoSang" value="Tipo Sanguineo" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="tipoSanguineo"
									value="#{receptor.tipoSanguineo}" /&gt;
							&lt;/rich:column&gt;

							&lt;rich:column&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="rh" value="RH" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="tipoRh" value="#{receptor.tipoRh}" /&gt;
							&lt;/rich:column&gt;

							&lt;rich:column&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="nomeHospital" value="Nome Hospital" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="nomeHosp" value="#{receptor.nomeHospital}" /&gt;
							&lt;/rich:column&gt;

							&lt;rich:column&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="numPront" value="Num Prontuário" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="numeroProntuario"
									value="#{receptor.numProntuario}" /&gt;
							&lt;/rich:column&gt;
						&lt;/rich:collapsibleSubTable&gt;

						&lt;rich:collapsibleSubTable value="#{_atendimentos.doadores}"
							var="doador" expandMode="client" id="sbtbldoad"&gt;
							&lt;rich:column colspan="6"&gt;
								&lt;f:facet name="header"&gt;
									&lt;h:outputText id="numBolsaDoador"
										value="Numero de bolsas dos doadores" /&gt;
								&lt;/f:facet&gt;
								&lt;h:outputText id="numBolsaDoad" value="#{doador.numBolsa}" /&gt;
							&lt;/rich:column&gt;
						&lt;/rich:collapsibleSubTable&gt;

						&lt;f:facet name="footer"&gt;
							&lt;rich:dataScroller for="atendimentosTable" maxPages="13"
								stepControls="hide" /&gt;
						&lt;/f:facet&gt;
					&lt;/rich:dataTable&gt;
				&lt;/h:panelGroup&gt;
			&lt;/rich:panel&gt;
		&lt;/f:view&gt;
	&lt;/ui:define&gt;
&lt;/ui:composition&gt;
&lt;/html&gt;

Método para paginar:

	public DataModel getDataModel() throws RegraNegocioException {
		int totalListSize = 0;

		int first = 0;
		int rows = 0;
		if (uiDataTable != null) {
			first = uiDataTable.getFirst();
			rows = uiDataTable.getRows();
		}
		List&lt;Atendimento&gt; atendimentos = new AtendimentoService().listarTodos(first, rows);
		totalListSize = new AtendimentoService().count();
		dataModel = new PagedDataModel(atendimentos, totalListSize, rows);
		return dataModel;
	}