View Javadoc

1   /*
2    * Copyright 2004-2005 Emmanouil Batsis
3    * 
4    * Licensed under the GNU General Public License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.gnu.org/licenses/gpl.txt
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   * 
16   *
17   * Created on 7 Sep 2004
18   */
19  package com.geekologue.md4j.dao.hibernate;
20  
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  //import org.apache.log4j.Logger;
25  import com.geekologue.md4j.dao.DataAccessException;
26  import com.geekologue.md4j.dao.Page;
27  import com.geekologue.md4j.util.Md4jException;
28  import org.hibernate.Criteria;
29  import org.hibernate.HibernateException;
30  import org.hibernate.Query;
31  import org.hibernate.Session;
32  import org.hibernate.criterion.CriteriaSpecification;
33  import org.hibernate.criterion.Projections;
34  
35  /***
36   * <p>
37   * Hibernate-based implementation of the Page interface. Used by Hibernate-based DAOs to 
38   * page Hibernate Queries, Criteria Queries as well as POJO collection references. 
39   * </p>
40   * <p>
41   * Paging a query is as simple as:
42   * </p>
43   * <blockquote>
44   * 
45   * <pre>
46   * Page page = new HbmQueryResultPage(session, session
47   *                   .createQuery(&quot;from Type obj order by obj.prop desc&quot;),
48   *                   pageNumber, maxResults);
49   * </pre>
50   * 
51   * </blockquote>
52   * <p>
53   * Which will only load maxResults+1 objects from the database. You can also
54   * page a collection like:
55   * </p>
56   * <blockquote>
57   * 
58   * <pre>
59   * Page page = new HbmQueryResultPage(session, object.getChildren(), pageNumber,
60   *                   maxResults);
61   * </pre>
62   * 
63   * </blockquote>
64   * 
65   * @author Manos Batsis 7 Sep 2004
66   * @version $Revision$
67   */
68  public class HbmQueryResultPage implements Page {
69      /***
70       * 
71       */
72      private static final long serialVersionUID = 1L;
73  
74      //private static Logger log              = Logger.getLogger(HbmQueryResultPage.class);
75  
76      private List          list             = null;
77  
78      private Map           parentOptions    = null;
79  
80      private int           pageNumber       = -1;
81  
82      private int           pageSize         = -1;
83  
84      private int           totalResultCount = -1;
85  
86      private int           totalPageCount   = -1;
87  
88      private boolean       first;
89  
90      private boolean       last;
91  
92      private int           firstResultIndex;
93  
94      private int           lastResultIndex;
95  
96      private int           previous;
97  
98      private int           next;
99  
100     /***
101      * Constructor that will page a query without loading all results
102      * 
103      * @param session
104      *            The hibernate session that created the query
105      * @param query
106      *            The query with any (named or positional) parameters already
107      *            set
108      * @param number
109      *            The page number (1,2,3...n)
110      * @param size
111      *            The number of entries a page should contain
112      */
113     public HbmQueryResultPage(Query query, int number,
114             int size) {
115         try {
116             // query.setCacheable(true);
117             // count total query results
118             this.totalResultCount = query.list().size();
119             this.firstResultIndex = (number - 1) * size;
120             query.setFirstResult(this.firstResultIndex);
121             // try to load one extra item to see if
122             // a next page exists
123             query.setMaxResults(size + 1);
124             this.list = query.list();
125             this.pageNumber = number;
126             this.totalPageCount = (int) Math.ceil((float) this.totalResultCount
127                     / (float) size);
128             this.first = this.pageNumber == 1;
129             this.last = this.list.size() <= size;
130             // we have checked if this is the last page,
131             // and can safely remove the uneeded last item
132             // used to do the check
133             if (this.list.size() > size) {
134                 this.list.remove(this.list.size() - 1);
135             }
136         } catch (Md4jException e) {
137             throw e;
138         } catch (Exception e) {
139             throw new DataAccessException(e);
140         }
141     }
142 
143     /***
144      * Constructor that will page a criteria query without loading all results
145      * 
146      * @param session
147      *            The hibernate session that created the query
148      * @param query
149      *            The criteria query
150      * @param number
151      *            The page number (1,2,3...n)
152      * @param size
153      *            The number of entries a page should contain
154      */
155     public HbmQueryResultPage(Criteria query,
156             Set projectionProps, int number, int size) {
157         try {
158             this.pageSize = size;
159             this.pageNumber = number;
160             // where to start the page from
161             this.firstResultIndex = (number - 1) * size;
162             // count total query results using criteria rowcount projection
163             this.totalResultCount = ((Integer) query.setProjection(
164                     Projections.rowCount()).uniqueResult()).intValue();
165             this.totalPageCount = (int) Math.ceil((float) this.totalResultCount
166                     / (float) size);
167             this.first = this.pageNumber == 1;
168             this.last = this.pageNumber == this.totalPageCount;
169             query.setFirstResult(this.firstResultIndex);
170             query.setMaxResults(size);
171             // set the criteria projection to either properties if requested or
172             // entities
173             if (projectionProps != null) {
174                 this.list = Helper.getQueryResultAsMapList(query,
175                         projectionProps);
176             } else {
177                 query.setProjection(null).setResultTransformer(
178                         CriteriaSpecification.ROOT_ENTITY);
179                 this.list = query.list();
180             }
181         } catch (Md4jException e) {
182             throw e;
183         } catch (Exception e) {
184             throw new DataAccessException(e);
185         }
186     }
187 
188     /***
189      * Constructor that will page a collection. Lazy collections are not initialized.
190      * <b>Untested.</b>
191      * 
192      * @param session
193      *            The Hibernate Session to use
194      * @param collection
195      *            The collection to page, e.g.: foo.getBars();
196      * @param pageNumber
197      *            The page number (1,2,3...n)
198      * @param pageSize
199      *            The number of entries a page should contain
200      * 
201      * @throws HibernateException
202      */
203     public HbmQueryResultPage(Session session, Set collection, int number,
204             int size) {
205         this(session.createFilter(collection, ""), number,
206                 size);
207     }
208 
209     /***
210      * @return Returns the total number of results the query can return, of
211      *         which this page instance contains a subset.
212      */
213     public int getTotalResultCount() {
214         return this.totalResultCount;
215     }
216 
217     /***
218      * Get the total number of pages that can be used to divide the total
219      * results using the current page size.
220      * 
221      * @return the total number of pages
222      */
223     public int getTotalPageCount() {
224         return this.totalPageCount;
225     }
226 
227     /***
228      * See if this page is the first
229      * 
230      * @return true if first, false otherwise
231      */
232     public boolean isFirst() {
233         return this.first;
234     }
235 
236     /***
237      * See if this page is the last
238      * 
239      * @return true if last, false otherwise
240      */
241     public boolean isLast() {
242         return this.last;
243     }
244 
245     /***
246      * Get the page of results as the list
247      * 
248      * @return the list containing the results for this page
249      */
250     public List getList() {
251         return this.list;
252     }
253 
254     /***
255      * Get the page number
256      * 
257      * @return the page number as given to the constructor
258      */
259     public int getPageNumber() {
260         return this.pageNumber;
261     }
262 
263     /*** Get the index of the first result in the page */
264     public int getFirstResultIndex() {
265         return this.firstResultIndex;
266     }
267 
268     /***
269      * return pageSize The page size
270      * 
271      * @return the page size
272      */
273     public final int getPageSize() {
274         return this.pageSize;
275     }
276 
277     /***
278      * @return Returns the last result index
279      */
280     public final int getLastResultIndex() {
281         return this.lastResultIndex;
282     }
283 
284     /***
285      * @return Returns the number of the next page
286      */
287     public final int getNext() {
288         return this.next;
289     }
290 
291     /***
292      * @return Returns the number of the previous page
293      */
294     public final int getPrevious() {
295         return this.previous;
296     }
297 
298     /***
299      * 
300      */
301     public void setList(List newList) {
302         this.list = newList;
303     }
304 
305     /***
306      * @see com.geekologue.md4j.dao.Page#getParentOptions()
307      */
308     public Map getParentOptions() {
309         return this.parentOptions;
310     }
311 
312     /***
313      */
314     public void setParentOptions(Map opts) {
315         this.parentOptions = opts;
316     }
317 }