Paginação com Hibernate

12 respostas
A

Bo dia senhores. :smiley:

Possuo um objeto A que possui uma collection de objetos B
Um determinado objeto A, possui quase 40.000 objetos B, e tenho uma tela que lista todos ops objetos B. O grande problema é que 40.000 objetos selecionados na tela acaba dando OutOfMemory.
O mais correto seria fazer uma paginação, onde selecionaria apenas 50 objetos B de cada vez.
Pergunta: Como posso fazer isso, se ao executar a.getBs(), o Hibernate já me retorna tudo de uma vez ? Com query nativa, eu faria os selects em lote e tudo se resolveria, mas não gostaria de criar essas consultas nativas.

Como posso resolver isto ?

12 Respostas

M

Ola Vc pode fazer assim:

Contando os registros.

StringBuilder sbQuery = new StringBuilder();
Query query = null;

sbQuery.append("select count(*) from sua_classe_B");

//getSession, retorna a sessão do hibernate
//Neste caso estou considerando que vc ja a possui
query = getSession().createQuery(sbQuery.toString());

Long count = (Long)query.uniqueResult();

//O numero count, é o numero de registros que vc possui, para que vc possa fazer a paginação.

Paginado o seu resultado:

Query query = null;
int maxRecords = 10;

sbQuery.append("from sua_classe_B");

/*Esta linha indica a partir de qual registo sua lista sera formada
Como maxRecords == 10, estou pegando o que seria a pagina 2.
onde o meu primeiro registro é 1 * 10 = 10 
A primeira pagina seria 0*10 = 0
*/
query.setFirstResult(1 * maxRecords);

/*Esta linha indica quantos registros serão recuperados a partir do primeiro*/
query.setMaxResults(maxRecords);

list = query.list();

Qualquer duvida poste ai...

A

Sim, via HQL é possivel, mas seria possivel SEM HQL ?

G

O que seria sem HQL? Com criteria?

R

Não sei se é exatamente isso que você procura, mas talvez a técnica de “batch fetching” seja útil:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html#performance-fetching-batch

A

O que seria sem HQL? Com criteria?

Não necessariamente.
Por exemplo, hoje, quando executo a.getBs(), ele me traz todos os objetos B dentro de A. Isso não é feito via HQL, é feito automático pelo mapeamento.
Estou procurando uma alternativa para trazer os mesmos objetos B, executando por exemplo getBs(1, 50), para pegar do primeiro ao quinquagésimo.

Minha duvida é, este tipo de coisa só é possivel via HQL/Criteria, ou é possivel fazer via mapeamento, como o link que roger_rf passou ?

M

Ola,

O mapeamento do objeto A ou B individualmente, vc pode fazer da forma de exemplifiquei ou por Criteria...
Se quizer te mostro como fazer com Criteria,

Porem se vc tem este cenario:

class B

class A{
     //Anotações de mapeamento aqui
     List<B> filho;
}

Então neste cenario, acredito que não tenha como vc paginar a lista B,
Posso estar dizendo besteira, mas pense bem:
Como vc ia estar limitando o numero de registros dentro desta lista?
No minimo o hibernate teria que manter a sessão aberta e ficar gerenciando sua interação em tempo de execução...
Não sei se tem como vc fazer isso...

O que vc pode estar fazendo é remover o objeto filho, e tratalo de forma independente.
Porem ja te adiantando, se vc fizer por exemplo um combo com sujestion, ficara pesado tambem.

Mas se a sua consulta estiver muito pesada com o objeto filho dentro de A, vc pode remove-lo...(dependendo de como estão suas telas e relacionamentos)

G

Pelo que entendi você quer fazer paginação pelo mapeamento one-to-many da entidade, e isso não é possível.

Na verdade eu sou bem contra fazer mapeamentos bidirecionais to tipo one-to-many e many-to-one. No seu caso A possui muitos B, então o correto é A não possuir a coleção de B, mas B possuir a referencia para A. Não gosto de usar A.getB, e um dos motivos é de você carregar todos os registros da base sem distinção.

O mais correto, na minha opinião, é você fazer uma chamada via HQL ou criteria para trazer todos os B que são filhos de A com seus critérios de paginação.

T

eu tb estava algum tempo atrás procurando uma resposta para isso, acabei construindo via query mesmo…

T

Foi por coincidência que eu achei isso na documentação do hibernate, veja se adianta

10.4.1.5. Pagination 
If you need to specify bounds upon your result set (the maximum number of rows you want to retrieve and / or the first row you want to retrieve) you should use methods of the Query interface: 

Query q = sess.createQuery("from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();
T

funcionalmente é desse modo que se faz, porém o que procurei ( e ainda procuro ) é algum mecanismo que faça para um relacionamento 1:N lazy, por exemplo a paginação automática, sem a necessidade de criar uma hql ou uma critéria para isso.

G

Se você ler meu comentário verá que realmente usando o relacionamento não há como fazer paginado. E se você procurar na community manuals do hibernate (não sei se no site do jboss novo tem isso) há um post que fala exatamente do quão ruim é você ter bidirecionais e sair usando algo como:

Customer customer = session.load(Customer.class, 1); List&lt;Debit&gt; Debits = customer.getDebits();

Sendo que o correto é você ter apenas o relacionamento de Debit.customer, e não ambos Debit.customer e Customer.debits.

A forma mais correta de você paginar é usar o que o Thiago indicou no post dele. Formula mágina para paginação você não irá encontrar.

T

Se você ler meu comentário verá que realmente usando o relacionamento não há como fazer paginado. E se você procurar na community manuals do hibernate (não sei se no site do jboss novo tem isso) há um post que fala exatamente do quão ruim é você ter bidirecionais e sair usando algo como:

Customer customer = session.load(Customer.class, 1); List&lt;Debit&gt; Debits = customer.getDebits();

Sendo que o correto é você ter apenas o relacionamento de Debit.customer, e não ambos Debit.customer e Customer.debits.

A forma mais correta de você paginar é usar o que o Thiago indicou no post dele. Formula mágina para paginação você não irá encontrar.

Não estamos falando necessariamente em relacionamentos bidirecionais, muito pelo contrário.
A meu ver essa paginação AINDA é burra, força meu código “cliente” a fazer iterações baseadas em limites da query. Em termos de código LIMPO, essa é a pior solução, mas até onde encontrei é a única.

Isso geralmente resolve os problemas de sistemas onde ALGUMAS queries podem “explodir” a memória, mas torna sistemas com monstruosas quantidades de informações labirintos lógicos.

E esse é somente uma das limitações do hibernate. Existem uma série de outras, como a não inclusão de tipos PK (simples ou compostas) e qualquer relacionamento em queries do tipo example, por exemplo…

Criado 8 de abril de 2010
Ultima resposta 12 de abr. de 2010
Respostas 12
Participantes 6