@@ -46,7 +46,7 @@ public class Paginator<T extends Model> implements Serializable {
46
46
private final MetaModel metaModel ;
47
47
private int currentPage ;
48
48
private final boolean fullQuery ;
49
- private final String countQuery ;
49
+ private final String countQueryFull ;
50
50
private boolean suppressCounts ;
51
51
private Long count = 0L ;
52
52
@@ -55,7 +55,14 @@ public class Paginator<T extends Model> implements Serializable {
55
55
* Convenience constructor. Calls {@link #Paginator(Class, int, String, Object...)} and passes true for <code>suppressCounts</code>.
56
56
*/
57
57
public Paginator (Class <? extends T > modelClass , int pageSize , String query , Object ... params ) {
58
- this (modelClass , pageSize , false , query , params );
58
+ this (modelClass , pageSize , false , query , null , params );
59
+ }
60
+
61
+ /**
62
+ * Convenience constructor. Calls {@link #Paginator(Class, int, String, Object...)} and passes null for <code>countQuery</code>.
63
+ */
64
+ public Paginator (Class <? extends T > modelClass , int pageSize , boolean suppressCounts , String query , Object ... params ) {
65
+ this (modelClass , pageSize , suppressCounts , query , null , params );
59
66
}
60
67
61
68
/**
@@ -87,7 +94,7 @@ public Paginator(Class<? extends T> modelClass, int pageSize, String query, Obje
87
94
* query should not contain limit, offset or order by clauses of any kind, Paginator will do this automatically.
88
95
* This parameter can have two forms, a sub-query or a full query.
89
96
*/
90
- public Paginator (Class <? extends T > modelClass , int pageSize , boolean suppressCounts , String query , Object ... params ) {
97
+ public Paginator (Class <? extends T > modelClass , int pageSize , boolean suppressCounts , String query , String countQuery , Object ... params ) {
91
98
92
99
this .suppressCounts = suppressCounts ;
93
100
try {
@@ -102,21 +109,27 @@ public Paginator(Class<? extends T> modelClass, int pageSize, boolean suppressCo
102
109
String tableName = Registry .instance ().getTableName (modelClass );
103
110
this .metaModel = metaModelFor (tableName );
104
111
112
+
105
113
this .fullQuery = DB .SELECT_PATTERN .matcher (query ).find ();
106
114
if (fullQuery ) {
107
115
Matcher m = FROM_PATTERN .matcher (query );
108
116
if (!m .find ()) {
109
117
throw new IllegalArgumentException ("SELECT query without FROM" );
110
118
}
111
- this .countQuery = metaModel .getDialect ().selectCount (query .substring (m .end ()));
119
+ String from = query .substring (m .end ());
120
+ if (countQuery != null ) {
121
+ this .countQueryFull = "SELECT " + countQuery + " FROM " + from ;
122
+ } else {
123
+ this .countQueryFull = metaModel .getDialect ().selectCount (from );
124
+ }
112
125
} else if (query .equals ("*" )) {
113
126
if (params .length == 0 ) {
114
- this .countQuery = metaModel .getDialect ().selectCount (tableName );
127
+ this .countQueryFull = metaModel .getDialect ().selectCount (tableName );
115
128
} else {
116
129
throw new IllegalArgumentException ("cannot provide parameters with query: '*'" );
117
130
}
118
131
} else {
119
- this .countQuery = metaModel .getDialect ().selectCount (tableName , query );
132
+ this .countQueryFull = metaModel .getDialect ().selectCount (tableName , query );
120
133
}
121
134
}
122
135
@@ -223,10 +236,10 @@ private LazyList<T> findAll() {
223
236
public Long getCount () {
224
237
if (count == 0L || !suppressCounts ) {
225
238
if (metaModel .cached ()) {
226
- count = (Long ) QueryCache .instance ().getItem (metaModel .getTableName (), countQuery , params );
239
+ count = (Long ) QueryCache .instance ().getItem (metaModel .getTableName (), countQueryFull , params );
227
240
if (count == null || count == 0 ) {
228
241
count = doCount ();
229
- QueryCache .instance ().addItem (metaModel .getTableName (), countQuery , params , count );
242
+ QueryCache .instance ().addItem (metaModel .getTableName (), countQueryFull , params , count );
230
243
}
231
244
} else {
232
245
count = doCount ();
@@ -239,10 +252,104 @@ public Long getCount() {
239
252
}
240
253
241
254
private Long doCount () {
242
- return Convert .toLong (new DB (metaModel .getDbName ()).firstCell (countQuery , params ));
255
+ return Convert .toLong (new DB (metaModel .getDbName ()).firstCell (countQueryFull , params ));
243
256
}
244
257
245
258
public int getPageSize () {
246
259
return pageSize ;
247
260
}
261
+
262
+ /**
263
+ * Use to create a paginator instance, and provide arguments as needed.
264
+ * @return self.
265
+ */
266
+ public static PaginatorBuilder instance (){
267
+ return new PaginatorBuilder ();
268
+ }
269
+
270
+
271
+ /**
272
+ * Provides a builder pattern to create new instances of paginator.
273
+ */
274
+ static class PaginatorBuilder <T extends Model >{
275
+ private Class <? extends T > modelClass ;
276
+ private int pageSize ;
277
+ private boolean suppressCounts = false ;
278
+ private String query ;
279
+ private String countQuery = null ;
280
+ private Object [] params ;
281
+
282
+ /**
283
+ * Model class mapped to a table.>
284
+ *
285
+ * @param modelClass Model class mapped to a table.>
286
+ * @return self
287
+ */
288
+ public PaginatorBuilder modelClass (Class <T > modelClass ){
289
+ this .modelClass = modelClass ;
290
+ return this ;
291
+ }
292
+
293
+ /**
294
+ * Page size - number of items in a page
295
+ *
296
+ * @param pageSize Page size - number of items in a page
297
+ */
298
+ public PaginatorBuilder pageSize (int pageSize ){
299
+ this .pageSize = pageSize ;
300
+ return this ;
301
+ }
302
+
303
+ /**
304
+ * Suppress calling "select count(*)... " on a table each time. If set to true,
305
+ * it will call count only once. If set to false, it will call count each time
306
+ * {@link #getCount()} is called from {@link #hasNext()} as well.
307
+ *
308
+ * @param suppressCounts suppress counts every time.
309
+ */
310
+ public PaginatorBuilder suppressCounts (boolean suppressCounts ){
311
+ this .suppressCounts = suppressCounts ;
312
+ return this ;
313
+ }
314
+
315
+
316
+ /**
317
+ * @param query Query that will be applied every time a new page is requested; this
318
+ * query should not contain limit, offset or order by clauses of any kind, Paginator will do this automatically.
319
+ * This parameter can have two forms, a sub-query or a full query.
320
+ */
321
+ public PaginatorBuilder query (String query ) {
322
+ this .query = query ;
323
+ return this ;
324
+ }
325
+
326
+ /**
327
+ * Part of the query that is responsible for count. Example: <code>COUNT(DISTINCT(u.id)</code>.
328
+ * Only use this method if you need something more complex than <code>COUNT(*)</code>, since
329
+ * that is the value that us used by default.
330
+ *
331
+ * @param countQuery Part of the query that is responsible for "count. Example: <code>count(*)</code>" or <code>COUNT(DISTINCT(u.id)</code>.
332
+ */
333
+ public PaginatorBuilder countQuery (String countQuery ) {
334
+ this .countQuery = countQuery ;
335
+ return this ;
336
+ }
337
+
338
+ /**
339
+ * Array of parameters in case a query is parametrized
340
+ * @param params Array of parameters in case a query is parametrized
341
+ */
342
+ public PaginatorBuilder params (Object ... params ){
343
+ this .params = params ;
344
+ return this ;
345
+ }
346
+
347
+ /**
348
+ * Terminal method to create an instance of Paginator.
349
+ * @return new Paginator properly configured.
350
+ */
351
+ public Paginator <T > create (){
352
+ return new Paginator <T >(modelClass , pageSize , suppressCounts , query , countQuery , params );
353
+ }
354
+ }
248
355
}
0 commit comments