paginacao.xml
13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" []>
<chapter id="paginacao">
<title>Paginação</title>
<para>
Neste capítulo serão considerados os seguintes assuntos:
<itemizedlist>
<listitem><para>Motivação para o uso de um mecanismo padronizado para <emphasis>paginação</emphasis>;</para></listitem>
<listitem><para>Funcionamento e uso da estrutura <literal>Pagination</literal> e do contexto de paginação
(<literal>PaginationContext</literal>).</para></listitem>
</itemizedlist>
</para>
<section>
<title>Introdução ao mecanismo</title>
<para>
A apresentação de conjuntos de registros de médio a grande porte em formato de tabelas em aplicações Web geralmente requer um
<emphasis>mecanismo de paginação</emphasis>, o qual permite ao cliente ver apenas um pedaço do resultado final, podendo este
navegar para frente e para trás através dos registros. A menos que o conjunto de registros seja garantidamente pequeno, qualquer
aplicação do tipo Web com funcionalidades de busca e/ou listagem de registros, precisa ser dotada de paginação.
</para>
<para>
O mecanismo de paginação para as aplicações fornecido pelo <emphasis>Demoiselle Framework</emphasis> consiste em um algoritmo
direcionado ao banco de dados (i.e., Database-Driven Pagination). Essa abordagem, apesar de requerer instruções SQL específicas
para obter porções determinadas de registros, é a mais utilizada por ser mais eficiente e produzir menos redundância de dados,
diminuindo assim tráfego de rede, consumo de memória e reduzindo o tempo de resposta.
</para>
<para>
É fornecido em tempo de execução um <emphasis>contexto de paginação</emphasis>, o qual tem escopo de sessão e armazena a informação
de paginação de cada entidade (i.e., bean) que necessite de tal mecanismo. Esse contexto é compartilhado entre as diversas camadas
da aplicação, especificamente entre as camadas de visão e persistência. Dessa maneira, a paginação dos dados é transparente para a
camada intermediária (i.e., negócio) e não interfere na modelagem das classes de um projeto.
</para>
</section>
<section>
<title>Códigos de suporte</title>
<!--
<para>
// TODO: explicar Pagination e PaginationContext
</para>
-->
<para>
O <emphasis>mecanismo de paginação</emphasis> do <emphasis>Demoiselle Framework</emphasis> permite que os parâmetros para a consulta
no banco sejam configurados de forma bastante prática. Por outro lado, a consulta paginada ao banco já é feita pela extensão
<literal>demoiselle-jpa</literal>. Dessa forma, basta ajustar os parâmetros da paginação, e pedir as consultas normalmente.
O resultado da consulta é então passado para algum componente de iteração de dados com suporte ao mecanismo conhecido como <emphasis>Lazy
Load</emphasis> (ou <emphasis>Lazy Loading</emphasis>).
</para>
<para>
Farão parte do código de suporte para paginação:
<itemizedlist>
<listitem>
<para>
A classe <literal>Pagination</literal>: usada para manipular a paginação dos dados resultantes, contendo os campos <literal>currentPage</literal>
(página atual, selecionada na camada de visão), <literal>pageSize</literal> (tamanho da página, a quantidade de registros que ela comportará)
e <literal>totalResults</literal> (a quantidade de resultados existentes na base de dados);
</para>
</listitem>
<listitem>
<para>
A classe <literal>PaginationContext</literal>: contexto usado para armazenar e fornecer estruturas do tipo <literal>Pagination</literal>;
</para>
</listitem>
<listitem>
<para>
A classe <literal>PaginationConfig</literal>: armazenador de configurações referentes à paginação.
</para>
</listitem>
</itemizedlist>
</para>
<para>Códigos internos de suporte no Core:</para><programlisting role="JAVA"><![CDATA[public class Pagination {
private int currentPage;
private int pageSize;
private Long totalResults;
private Integer totalPages;
// ...
}
@SessionScoped
public class PaginationContext {
private final Map<Class<?>, Pagination> map;
public Pagination getPagination(Class<?> clazz) { ... }
public Pagination getPagination(Class<?> clazz, boolean create) { ... }
}
@Configuration
public class PaginationConfig {
@Name("default_page_size")
private int defaultPageSize = 10;
@Name("max_page_links")
private int maxPageLinks = 5;
}]]></programlisting>
<para>Códigos internos de suporte em JPA:</para><programlisting role="JAVA"><![CDATA[public class JPACrud<T, I> implements Crud<T, I> {
@Inject
private PaginationContext paginationContext;
// ...
public List<T> findAll() {
final String jpql = "select this from " + getBeanClass().getSimpleName() + " this";
final Query query = getEntityManager().createQuery(jpql);
final Pagination pagination = paginationContext.getPagination(getBeanClass());
if (pagination != null) {
if (pagination.getTotalPages() == null) {
pagination.setTotalResults(this.countAll());
}
query.setFirstResult(pagination.getFirstResult());
query.setMaxResults(pagination.getPageSize());
}
// ...
}
}]]></programlisting>
<para>Códigos internos de suporte em JSF:</para><programlisting role="JAVA"><![CDATA[public abstract class AbstractListPageBean<T, I> extends AbstractPage implements ListPageBean<T, I> {
@Inject
private PaginationContext paginationContext;
@Inject
private PaginationConfig paginationConfig;
// ...
public Pagination getPagination() {
return paginationContext.getPagination(getBeanClass(), true);
}
public int getPageSize() {
return paginationConfig.getDefaultPageSize();
}
public int getMaxPageLinks() {
return paginationConfig.getMaxPageLinks();
}
}]]></programlisting>
</section>
<section>
<title>Implementação na aplicação</title>
<para>
Veremos nessa seção como implementar a paginação em uma aplicação Java. Para esse exmplo tomamos como base a aplicação de Bookmarks
fornecida pelo arquétipo <emphasis>JSF com JPA</emphasis> do <emphasis>Demoiselle Framework</emphasis> (para maiores detalhes
ver <link linkend="estrutura">Arquétipos</link>). Iremos utilizar o componente <literal>DataTable</literal> do <emphasis>PrimeFaces</emphasis>,
que oferece o mecanismo de <emphasis>Lazy Loading</emphasis> conhecido como <literal>LazyDataModel</literal>, muito útil para paginação
e classificação de dados.
</para>
<para>
Primeiro é preciso configurar um objeto <literal>LazyDataModel</literal> no construtor do <emphasis>Managed Bean</emphasis>
(<emphasis>BookmarkList</emphasis> nesse exemplo): instancia-lo e sobrescrever o método abstrado <literal>load</literal>, que recebe
vários argumentos. Esses argumentos são recuperados na página <literal>jsf</literal> que carrega a instância do objeto <literal>LazyDataModel</literal>.
</para>
<para>
Dentro do método <literal>load</literal> iremos pegar do contexto de paginação uma instância da implementação da interface <literal>Pagination</literal>
e ajustar alguns dos seus parâmetros para: indicar a partir de qual item a paginação deve iniciar, e o tamanho (quantidade de itens) de cada página.
Esses dados são usados no método <literal>findAll()</literal>, da classe <literal>JPACrud</literal> (extensão JPA), que utiliza o contexto de
paginação para pegar os parâmetros e fazer a consulta no banco buscando apenas os itens que estão dentro da página que o parâmetro
<literal>first</literal> indicar. O resultado é passado para a instancia do <literal>LazyDataModel</literal>, que é responsável por exibir
os dados de forma apropriada.
</para>
<para>
À classe <emphasis>BookmarkList</emphasis> devem ser adicionados os seguintes trechos de código:
</para><programlisting role="JAVA"><![CDATA[// ...
import java.util.Map;
import br.gov.frameworkdemoiselle.pagination.Pagination;
// ...
public BookmarkListMB() {
private LazyDataModel<Bookmark> lazyModel;
lazyModel = new LazyDataModel<Bookmark>() {
@Override
public List<Bookmark> load (int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, String> filters){
Pagination pagination = getPagination();
pagination.setPageSize(pageSize);
pagination.setFirstResult(first);
List<Bookmark> itemsList = bc.findAll();
lazyModel.setRowCount(pagination.getTotalResults());
return itemsList;
}
};
// ...
public LazyDataModel<Bookmark> getLazyModel() {
return lazyModel;
}
// ...
}]]></programlisting>
<para>
No arquivo <literal>messages.properties</literal> adicione as linhas:
</para><programlisting role="JAVA"><![CDATA[page.first=0
page.rows=4
page.max.links=3]]></programlisting>
<para>
Na página JSF <literal>bookmark_list.xhtml</literal>, substitua a linha:
</para>
<programlisting role="JAVA"><![CDATA[<p:dataTable id="list" var="bean" value="#{bookmarkListMB.resultList}">]]></programlisting>
<para>
por:
</para><programlisting role="JAVA"><![CDATA[<p:dataTable id="list" var="bean"
value="#{bookmarkListMB.lazyModel}" lazy="true" paginator="true"
first="#{messages['page.first']}" rows="#{messages['page.rows']}"
pageLinks="#{messages['page.max.links']}">]]></programlisting>
<para>
Com essas alterações simples, a aplicação Bookmarks passa a utilizar o mecanismo de paginação oferecido pelo <emphasis>Demoiselle Framework</emphasis>.
</para>
<tip>
<para>
O método <literal>getPagination()</literal> do contexto <literal>PaginationContext</literal> é sobrecarregado, podendo aceitar
os seguintes argumentos: <literal>Class</literal> ou <literal>Class</literal> e <literal>boolean</literal>.
</para>
</tip>
<note>
<para>
A JPA 2.0, através da Query API, suporta controle de paginação independente de fornecedor de banco de dados.
Para controlar a paginação, a interface <literal>Query</literal> define os métodos <literal>setFirstResult()</literal> e
<literal>setMaxResults()</literal> para especificar o primeiro resultado a ser recebido e o número máximo de resultados a
serem retornados em relação àquele ponto. Internamente, são usadas instruções específicas do SGBD (ex: LIMIT e OFFSET no
PostgreSQL).
</para>
</note>
</section>
</chapter>
<!--<section>
<title>Um exemplo usando PrimeFaces</title>
<para>
Eis um interessante caso de uso de paginação: ????.
A seguir o código referente a essa implementação:
// camada de persistência
</para>
<programlisting role="JAVA"><![CDATA[@PersistenceController
public class AuditDAO extends JPACrud<Audit, Long> {
}
]]></programlisting>
<para>
...
// camada de negócio
</para>
<programlisting role="JAVA"><![CDATA[@BusinessController
public class AuditBC extends DelegateCrud<Audit, Long, AuditDAO> {
}
]]></programlisting>
<para>
...
// camada de visão
</para>
<programlisting role="JAVA"><![CDATA[import org.primefaces.model.LazyDataModel;
...
@ViewController
public class AuditMB extends AbstractListPageBean<Audit, Long> {
@Inject
private IAuditBC bc;
private LazyDataModel<Audit> lazyModel;
public AuditMB() {
lazyModel = new LazyDataModel<Audit>() {
public List<Audit> load(int first, int pageSize, String sortField,
boolean sortOrder, Map<String, String> filters) {
Pagination pagination = getPagination();
pagination.setFirstResult(first);
pagination.setPageSize(pageSize);
return bc.findAll();
}
};
}
public LazyDataModel<Audit> getLazyModel() {
return lazyModel;
}
}]]></programlisting>
<para>
??? exemplo de uso de paginação em tabela com a implementação PrimeFaces:
</para>
<programlisting role="XML"><![CDATA[<p:dataTable id="list" height="300" var="bean"
value="#{auditMB.lazyModel}" lazy="true" paginator="true"
rows="#{auditMB.pageSize}" pageLinks="#{auditMB.maxPageLinks}">]]></programlisting>
</section>
-->
<!--
<section>
<title>Referências</title>
<itemizedlist>
<listitem>JPA 2.0 [JSR-317] (http://jcp.org/en/jsr/detail?id=317)</listitem>
<listitem>Implementing Search Result Pagination in a Web Application (http://www.developer.com/java/other/article.php/3696226/)</listitem>
<listitem>A Pagination Technique Using Spring (http://www.developer.com/java/web/article.php/10935_3830886_1/)</listitem>
<listitem>Spring JDBC Pagination Tutorial (http://www.codefutures.com/tutorials/spring-pagination/)</listitem>
<listitem>PrimeFaces DataTable - Lazy Loading (http://www.primefaces.org/showcase/ui/datatableLazy.jsf)</listitem>
</itemizedlist>
</section>
-->