Esse post sobre
paginação com hibernate, considero que você já tenha seu DAO, ManagedBean, o
Bean e a página XHTML feita, e quer apenas mudar a forma como os dados são
trazidos do banco, melhorando dessa forma o desempenho do sistema.
Para
fazermos paginação com o Hibernate, precisamos preparar a camada View (Páginas XHTML) para lidar com
essa paginação. No caso do primefaces, isso é bem simples. Vamos analisar como
fica o componente <p:dataTable> de
forma a trabalhar com a paginação:
<p:dataTable id="carroTable"
paginator="true" rows="20" lazy="true" paginatorAlwaysVisible="false"
paginatorPosition="bottom" var="carro"
value="#{pesquisaCarroBean.lazyCarros}" rowsPerPageTemplate="10,20,50"
emptyMessage="Nenhum carro encontrado." style="margin-top: 20px" >
...
<p:dataTable>
A propriedade paginator=”true”
indica que haverá um paginação, e mesmo que não fizéssemos a parte de paginação
na camada Model, o primefaces conseguiria paginar os dados trazidos para esse
dataTable, porém, mesmo a exibição
estando paginada, todos os dados
estariam carregados em memória. Daí a importância de fazermos realmente a
paginação dos dados, mudando a forma como esses dados são trazidos do banco de
dados, que é o que vamos ver mais a frente nesse post.
A propriedade paginatorAlwaysVisible=”false”
serve para que o paginador aparece apenas quando houver dados suficientes para
serem paginados. O paginatorPosition=”bottom”
informa a posição que a paginação será exibida. A propriedade rows=”20” indica quantas linhas serão
exibidas por default, já a propriedade rowsPerPageTemplate=”10,20,50”
é responsável por permitir a customização da quantidade de linhas
visualizadas.
Porém, a propriedade lazy=”true”
é quem realmente faz o componente do primefaces entender que haverá uma
paginação real dos dados. Com isso, podemos passar para a camada Model.
Precisamos criar uma classe responsável por fazer o meio de
campo entre o componente do primefaces
que será paginado e o método responsável por buscar a consulta paginada usando
o hibernate. Vamos começar criando a
classe LazyCarro, que criei dentro
de um novo pacote chamado lazy_model dentro
da minha hierarquia de pacotes. Segue o código:
public class LazyCarro extends LazyDataModel<Carro> implements Serializable{
private static final long serialVersionUID = 1L;
private CarroDAO carroDAO;
public LazyCarro(CarroDAO carroDAO) {
this.carroDAO =
carroDAO;
}
@Override
public List<Carro> load(int first, int pageSize,
String
sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<Carro>
carros = carroDAO.buscarTodosPaginado(first, pageSize);
this.setRowCount(carroDAO.pegarQuantidadeDeCarros().intValue());
return carros;
}
}
Essa classe estende a classe LazyDataModel do primefaces, e
tem uma implementação bem simples. Preste bastante atenção quanto ao método load que deve ser sobrescrito,
que deve ser o segundo e não o primeiro. Note que os dois métodos que chamamos
do carroDAO (buscarTodosPaginado(first, pageSize) e pegarQuantidadeDeCarros()) precisam
ser criados, então vamos adicioná-los a classe CarroDAO.
public List<Carro>
buscarTodosPaginado(int primeiroDigito, int segundoDigito){
return em.createQuery("FROM Carro", Carro.class)
.setFirstResult(primeiroDigito)
.setMaxResults(segundoDigito)
.getResultList();
}
public Long pegarQuantidadeDeCarros(){
return em.createQuery("SELECT count(c) FROM Carro
c", Long.class)
.getSingleResult();
}
Agora
que já temos tudo feito na camada model, vamos a camada Controller que é responsável por fazer a ligação entre a View e a
Model. Na classe PesquisaCarroBean
vamos adicionar o objeto do tipo LazyCarro
que será inicializado no método init
que possui a anotação @PostConstruct
e também o seu método getter para
que a página XHTML possa ter acesso.
@ManagedBean
@ViewScoped
public
class PesquisaCarroBean implements Serializable{
private LazyCarro lazyCarros;
...
public LazyCarro getLazyCarros() {
return
lazyCarros;
}
...
@PostConstruct
public void init(){
lazyCarros
= new LazyCarros(new
CarroDAO);
...
}
...
}
Pronto, com isso já temos uma consulta paginada. Caso apareça
um erro informando e o “Lazy loading não foi implementado”, sugiro que
verifique o método load que você
sobrescreveu na classe LazyCarro, checando os parâmetros com estes:
load(int first, int pageSize, String
sortField, SortOrder sortOrder, Map<String, Object> filters){...}
Qualquer dúvida,
pode deixar nos comentários que tentarei ajudar no que for possível...
Blz Daniel. Cara, gostei bastante da sua publicação. Qual a versão do PrimeFaces e Hibernate usadas? Além do JavaEE. Estou utilizando o seguinte código para poder fazer lazy na minha DataTable e preciso que o filter dela funcione. Como poderia fazer? Segue abaixo meu código até o momento.
ResponderExcluirView.xhtml #######
"
"
#######
@ManagedBean(name="valoresTFJBean")
//@RequestScoped
@ViewScoped
public class ValoresTFJBean implements Serializable{
private static final long serialVersionUID = 1L;
private Valores valor = new Valores();
private List lista = null;
private List listaAno = null;
private LazyDataModel lazyDataModel;
public Valores getValor() {
return valor;
}
public void setValor(Valores valor) {
this.valor = valor;
}
public List getLista() {
if(this.lista == null)
{
ValoresRN valoresRN = new ValoresRN();
this.lista = valoresRN.listar();
}
return this.lista;
}
public LazyDataModel getLazyDataModel() {
if(lazyDataModel == null){
ValoresRN valoresRN = new ValoresRN();
lazyDataModel = new ValoresTFJLazy(valoresRN.listar());
}
return lazyDataModel;
}
public List getListaAno() {
if(this.listaAno ==null){
ValoresRN valoresRN = new ValoresRN();
this.listaAno= valoresRN.listaAno();
}
return this.listaAno;
}
public List listarPorAno(Integer ano){
return new ValoresRN().listarPorAno(ano);
}
}
########
LazyDataTable ############
public class ValoresTFJLazy extends LazyDataModel {
private List lista;
public ValoresTFJLazy(List lista){
this.lista = lista;
}
@Override
public Valores getRowData(String rowKey) {
for(Valores valor : lista) {
if(valor.getCodigo().equals(rowKey))
return valor;
}
return null;
}
@Override
public Object getRowKey(Valores valor) {
return valor.getCodigo();
}
@Override
public List load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map filters) {
List data = new ArrayList();
//filter
for(Valores valor : lista) {
boolean match = true;
if (filters != null) {
for (Iterator it = filters.keySet().iterator(); it.hasNext();) {
try {
String filterProperty = it.next();
Object filterValue = filters.get(filterProperty);
String fieldValue = String.valueOf(valor.getClass().getField(filterProperty).get(valor));
if(filterValue == null || fieldValue.startsWith(filterValue.toString())) {
match = true;
}
else {
match = false;
break;
}
} catch(Exception e) {
match = false;
}
}
}
if(match) {
data.add(valor);
}
}
//sort
//if(sortField != null) {
// Collections.sort(data, new LazySorter(sortField, sortOrder));
//}
//rowCount
int dataSize = data.size();
this.setRowCount(dataSize);
//paginate
if(dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
}
catch(IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
}
else {
return data;
}
}
}
########
Deixa eu ver se entendi, você quer usar lazy na propriedade do Bean e quando for usar o filtro essa propriedade precisa ser carregada? É isso?
ExcluirSeria isso: Tenho uma table que usa lazy e tem filter no cabeçalho. Se não uso lazy o filter funciona. Se uso lazy o filter não funciona. Ficou mais claro? Desculpa ae se não deixei a pergunta clara.
ExcluirBom Yuri, quando usamos Lazy em uma propriedade de um Bean, estamos dizendo para o JPA: Não carregue essa propriedade para mim, a menos que eu ordene. Então toda vez que eu quiser trazer essa propriedade em um select, tenho que escrever um select que faça isso explicitamente dando um JOIN entre a tabela pricipal e a tabela a qual apropriedade pertence.
ExcluirPor exemplo: Se você tiver um Bean Pedido que tem uma propriedade lazy do tipo ItemPedido, na consulta você quer trazer campos de Pedido e de ItemPedido vai ter que fazer algo do tipo:
"SELEC NEW Pedido ( p.codigo, p.cliente, i.codigo, i.descricao, i.valor, i.desconto ) FROM Pedido p INNER JOIN p.ItemPedido i WHERE p.codigo = :codigo".
Note que nesse caso tenho que ter um construtor em Pedido preparado para receber esses parâmetros e montar os objetos e que terei que colocar um a um cada parâmetro que eu quiser trazer da consulta como uma consulta nativa.
Obrigado Daniel. Ajudou a esclarecer o caminho.
ExcluirValeu irmão, funcionou direitinho.
ResponderExcluirVc eh fera!
Valeu... :)
Excluirde onde surgiu aquele .....em.createQuery(...... Este "em" é oq?
ResponderExcluirÉ suposto que, no código, um objeto da classe EntityManager esteja disponível. No caso há um objeto de tal classe na variável "em".
ExcluirExatamente, é porque no começo da postagem eu disse que considerava que você já tenha seu DAO funcionando, e um DAO implica em um EntityManager disponível.
ExcluirPoxa, eu segui tudo mas sempre diz que não esta implementado.
ResponderExcluir“Lazy loading não foi implementado"
Tem que usar o debug pra ver. Verificar se está chegando no método que executa o SQL.
ExcluirVerificar os imports e se seu DAO está instanciado corretamente.