Paginação no hibernate

Como faço paginação no hibernate.
Precisa me retornar o número total de registros.
Outra coisa: Descobri que não dá para ordenar por composite-elements.
ex:


class Nota {
  private Set itens;
  // getter and setters
}

class ItemNota {
  private int qtd;
  private String descricao;
}

Nota.getItens()

Não posso ordenar em tempo de execução por qtd ou por descricao.

Ordene o Set, ue :?

setFirstResult
setMaxResults

Referente a ordenacao pode ser uma solução ordenar o Set na memória.
Se tiver exemplos…

[quote=jprogrammer]Referente a ordenacao pode ser uma solução ordenar o Set na memória.
Se tiver exemplos…
[/quote]

Faça os seus objetos implementarem a interface Comparable e em vez de colocar uma HashSet, coloque uma TreeSet como sendo a classe concreta da coleção.

Posso fazer isso ?

class Nota {
   private TreeSet itens;
   // getter and setters
 }

Se estiver utilizando Hibernate 3 e Criteria API, você pode utilizar Projection (Projections.rowCount()) para obter o total de registro sem considerar a paginação (isto é, antes de setar setFirstResult e setMaxResults).

Se estiver utilizando Hibernate 2 e Criteria API, e não puder migrar para Hibernate 3!, tem um work-around q permite usar Criteria.count(). É só baixar uma versão “Patcheada” do hibernate aqui: http://opensource.atlassian.com/projects/hibernate/browse/HB-474
Mas lembrando que esssa versão não é oficial…

Espero ter ajudado!

[quote=jprogrammer]Posso fazer isso ?
[/quote]

Eu faço assim:

class Nota {
   private Set itens = new TreeSet();
 }

Não dá “xábu” com o lazy-loading.

Assim eu tô instanciado o objeto desnecessariamente.

Ah velho, então transforme sua coleção num array e dê um sort(), não tem mágica não :smiley:

Nao precisa transformar em array - basta “abracar” o Set que vem do Hibernate (que eh de um tipo proprio do Hibernate, por causa do lazy loading) em um TreeSet ou qualquer outra implementacao de SortedSet, assim oh:

s = new TreeSet(comparator); s.addAll(setDoHibernate);

Ou, se os elementos implementam Comparable, o que eh altamente recomendado nesse caso:

E, claro:

http://java.sun.com/j2se/1.4.2/docs/api/java/util/TreeSet.html#TreeSet(java.util.Collection)

     Pessoal, estou também com problemas para paginar uma consulta, mas no meu caso é devido a quantidade de registros que preciso retornar. No meu caso, não tenho condições de trazer todos os registros que preciso em tempo hábil e por isso preciso buscar em partes. 
    O problema é que já usei tags para paginar e também "setFirstResult" e "setMaxResults" mas a impressão que tive comparando o tempo das buscas (com 5, 100 e 1000 registros, sempre foi o mesmo tempo) é que o hibernate sempre trazia todos os itens e depois filtrava o que eu havia passado como parâmetro, o q não resolve o problema...
    Já tentei usar lazy="true" para não precisar carregar toda a árvore de dados, mas preciso das informações dos nós filhos também...  :( 
    Alguém teria alguma solução para paginação com uma quantidade grande de registros que não seja usar diretamente o sql ? Pode ser algum esquema de offset no hql, que ainda não encontrei... :(
    Qualquer dica será muito bem-vinda ! Obrigado  :)

HHahahahahahA

Como eu fiquei de saco cheio de procurar coisas simples resolvi fazer hard code, só tem um problema,
como eu imprimo os links (código html) no struts 2?

método da classe de action

public String getAll(){
	G_Paginacao paginacao = new G_Paginacao();
	paginacao.setTotalRows(livroService.getTotalLivros());
	paginacao.setPagina(pagina);
	paginacaoLinks=paginacao.getLinks();
	livros = livroService.getListAll(paginacao.getFirstResultNumber(),paginacao.getMaxResults());
	return SUCCESS;
}

Minha classe de paginação

package br.com.goals.util;

import java.math.BigInteger;

public class G_Paginacao {
	int resultadosPorPagina = 5;

	int totalRows;

	int pagina = 0;

	int maxLinksDePaginas = 10;

	int totalPagina;

	String propriedade = "pagina";

	StringBuilder links = null;

	public G_Paginacao() {

	}

	public String getLinks() {
		if (links == null) {
			makeLinks();
		}
		return links.toString();
	}

	private void makeLinks() {
		links = new StringBuilder();
		totalPagina = totalRows / resultadosPorPagina;
		if (totalRows % resultadosPorPagina != 0) {
			totalPagina++;
		}
		int i, metade = resultadosPorPagina / 2;
		for (i = pagina - metade < 0 ? 0 : pagina - metade; i < maxLinksDePaginas
				&& i < totalPagina; i++) {
			links.append("<a href=\"?" + propriedade + "=" + i + "\">"
					+ (i + 1) + "</a>");
		}
	}

	public int getFirstResultNumber() {
		return pagina * resultadosPorPagina;
	}

	public int getTotalRows() {
		return totalRows;
	}

	public void setTotalRows(int totalRows) {
		this.totalRows = totalRows;
	}

	public int getPagina() {
		return pagina;
	}

	public void setPagina(Integer paginaAtual) {
		if (paginaAtual == null){
			this.pagina = 0;
		}else{
			this.pagina = paginaAtual;
		}
	}

	public String getPropriedade() {
		return propriedade;
	}

	public void setPropriedade(String propriedade) {
		this.propriedade = propriedade;
	}

	/**
	 * Se houver muitos resultados troque tudo para bigInteger
	 * 
	 * @param bigInteger
	 */
	public void setTotalRows(BigInteger bigInteger) {
		this.totalRows = Integer.valueOf(bigInteger.toString());
	}

	public int getMaxResults() {
		return resultadosPorPagina;
	}

}

Tentativa de imprimir os links no struts 2

<s:property value="paginacaoLinks" />

Ele imprime o código fonte > , etc. :slight_smile: LOL
mas cheguei perto heheheeh

A solução para o meu caso é criar um POJO de link

package br.com.goals.util;

public class Link {
	private String label;
	private String href;
	public Link(int i, int j) {
		label=String.valueOf(i);
		href=String.valueOf(j);
	}
	public String getHref() {
		return href;
	}
	public void setHref(String href) {
		this.href = href;
	}
	public String getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
}

e no JSP um iterator sobre os links :slight_smile:

<s:iterator value="paginacaoLinks">
	<s:url id="paginacao" action="livrosGetAll">
		<s:param name="pagina" value="href"/>
	</s:url>
	<s:a href="%{paginacao}"><s:property value="label"/></s:a>
</s:iterator>

trocamos o método makelinks e getLinks para retornar List

private void makeLinks() {
	links = new StringBuilder();
	totalPagina = totalRows / resultadosPorPagina;
	if (totalRows % resultadosPorPagina != 0) {
		totalPagina++;
	}
	int i, metade = maxLinksDePaginas / 2;
	int startAt =pagina - metade < 0 ? 0 : pagina - metade; 
	for (i = startAt;	i < startAt+maxLinksDePaginas && i < totalPagina; i++) {
		arrLinks.add(new Link(i+1,i));
	}
}

E pronto a paginação simples funciona.

mas enfim, o melhor é o display tag pelo que eu tenho pesquisado…

Eu estava analisando o sql gerado pelo hibernate/JPA no sql server usando setFirst e setMaxResults. Ele aparentemente trás todos até aquela quantidade e depois descarta os primeiros em memória não é isso?

Por exemplo se eu colocar um:

setFirstResult(100);
setMaxResult(10);

Na lista que eu vou receber vai estar apenas os 10 que começam a partir do 100, mas o sql irá buscar 110, correto? Isso me preocupou um pouco pois estou utilizando em projeto para fazer a paginação e que poderá ter milhares de itens conforme os critérios de pesquisa, e uma paginação pequena para melhorar o desempenho, mas se for isso mesmo o desempenho do ponto de vista do select no banco será a mesma coisa…

Existe alguma outra forma? tipo, parecido com o limit no MySQL

Você poderia postar qual foi a SQL gerada pelo Hibernate?

Acredito também que haja um cache e um proxy, ou seja, não é sempre que o banco de dados será acionado.

Outro dia estava pensando em uma forma para fazer estas paginações.
Se vc retornar uma consulta com 1.000.000 de resultados a formula para páginas seria
numeroDePaginas=totalDeRows/resultadoPorPagina;
se vc mostrar 10 resultados por página seriam 100.000 páginas
o que não caberia na tela (pagina 1,2,3 … 100.000)

E normalmente exibimos poucas páginas umas 10 com setas para mais e menos, como o google.

Pensando desta forma vc poderia limitar os resultados para no máximo 100 a partir do 0
na primeira consulta.

E conforme o usuário fosse paginando vc retornaria os 100 depois do id X.
Criando um where com criteria, ou qq outra coisa.

No SQL gerado ele é mais ou menos um:

select top 36 (campos) from (tabela) where (minhaCondicao)

Isso supondo que ele está com Max em 9 e first em 27.

[quote=fabioissamu]Você poderia postar qual foi a SQL gerada pelo Hibernate?

Acredito também que haja um cache e um proxy, ou seja, não é sempre que o banco de dados será acionado.

Outro dia estava pensando em uma forma para fazer estas paginações.
Se vc retornar uma consulta com 1.000.000 de resultados a formula para páginas seria
numeroDePaginas=totalDeRows/resultadoPorPagina;
se vc mostrar 10 resultados por página seriam 100.000 páginas
o que não caberia na tela (pagina 1,2,3 … 100.000)

E normalmente exibimos poucas páginas umas 10 com setas para mais e menos, como o google.

Pensando desta forma vc poderia limitar os resultados para no máximo 100 a partir do 0
na primeira consulta.

E conforme o usuário fosse paginando vc retornaria os 100 depois do id X.
Criando um where com criteria, ou qq outra coisa.
[/quote]

Na verdade a lógica da paginação está prontinha, e está funcionando perfeitamente, com essas setinhas, os pontinhos e tal. Mas o que me assustou foi o sql gerado que deu a impressão de que ele trouxe muita coisa do banco, mas jogou dentro do meu list apenas dentro da paginação que eu estabeleci.

Parece codigo de SQL Server,
talvez esteja trazendo mais coisas do que o necessário.
Não lembro os comandos nativos de SQL Server, mas tinha alguma coisa no final do comando, um rowcount qq coisa do genero.

Vc pode rodar a consulta no query analizer ou outro e ver?

Depois posta o resultado, é legal saber se tem alguma penalidade.

Ultimamente uso mais MySql.

[quote=fabioissamu]Parece codigo de SQL Server,
talvez esteja trazendo mais coisas do que o necessário.
[/quote]
Sim, foi o que eu disse no meu primeiro post…

Acho que não há necessidade. Com esse sql ele está trazendo 36 registros em vez de 9, que seria o ideal no meu caso…

Eu tbm prefiro o MySQL, mas isso não é uma escolha minha.

Vou continuar pesquisando uma solução.