View Javadoc

1   /***
2    * Copyright (c) 2003 held jointly by the individual authors.            
3    *                                                                          
4    * This library is free software; you can redistribute it and/or modify it    
5    * under the terms of the GNU Lesser General Public License as published      
6    * by the Free Software Foundation; either version 2.1 of the License, or 
7    * (at your option) any later version.                                            
8    *                                                                            
9    * This library is distributed in the hope that it will be useful, but 
10   * WITHOUT ANY WARRANTY; with out even the implied warranty of 
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
12   * GNU Lesser General Public License for more details.                                                  
13   *                                                                           
14   * You should have received a copy of the GNU Lesser General Public License   
15   * along with this library;  if not, write to the Free Software Foundation,   
16   * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.              
17   *                                                                            
18   * > http://www.gnu.org/copyleft/lesser.html                                  
19   * > http://www.opensource.org/licenses/lgpl-license.php
20   */
21  package net.mlw.vlh.adapter.hibernate3;
22  
23  import java.lang.reflect.InvocationTargetException;
24  import java.text.ParseException;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import net.mlw.vlh.DefaultListBackedValueList;
29  import net.mlw.vlh.ValueList;
30  import net.mlw.vlh.ValueListInfo;
31  import net.mlw.vlh.adapter.AbstractValueListAdapter;
32  import net.mlw.vlh.adapter.hibernate3.util.ScrollableResultsDecorator;
33  import net.mlw.vlh.adapter.hibernate3.util.StatementBuilder;
34  import net.mlw.vlh.adapter.util.ObjectValidator;
35  
36  import org.apache.commons.beanutils.PropertyUtils;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.hibernate.HibernateException;
40  import org.hibernate.Query;
41  import org.hibernate.ScrollableResults;
42  import org.hibernate.Session;
43  import org.hibernate.SessionFactory;
44  import org.springframework.orm.hibernate3.SessionFactoryUtils;
45  
46  /*** 
47   *  This adapter wraps the functionality of Hibernate.
48   *  Add extra functionality such as paging, focusing
49   *  and validating of current result set.
50   * <i>
51   *   "Hibernate is a powerful, ultra-high performance 
52   *   object/relational persistence and query service 
53   *   for Java. Hibernate lets you develop persistent 
54   *   classes following common Java idiom - including 
55   *   association, inheritance, polymorphism, composition 
56   *   and the Java collections framework. The Hibernate 
57   *   Query Language, designed as a "minimal" 
58   *   object-oriented extension to SQL, provides an 
59   *   elegant bridge between the object and relational 
60   *   worlds. Hibernate also allows you to express queries 
61   *   using native SQL or Java-based Criteria and Example 
62   *   queries. Hibernate is now the most popular 
63   *   object/relational mapping solution for Java."
64   * </i> -http://www.hibernate.org/
65   * 
66   * @author Matthew L. Wilson, Andrej Zachar
67   * @version $Revision: 1.2 $ $Date: 2006/03/29 19:47:49 $
68   */
69  public class HibernateAdapter extends AbstractValueListAdapter
70  {
71     /*** The Hibernate SessionFactory. */
72     private SessionFactory sessionFactory;
73  
74     /***
75      * <p>
76      * If is set, it use special ScrollableResultsDecorator, that enable or
77      * disable to add object in final list.
78      * </p>
79      * <h4>NOTE:</h4>
80      * <p>
81      * Also, it respects the total count of entries that overlap your paged
82      * list.
83      * </p>
84      */
85     private ObjectValidator _validator = null;
86  
87     /*** Commons logger. */
88     private static final Log LOGGER = LogFactory.getLog(HibernateAdapter.class);
89  
90     /*** If a new Session should be created if no thread-bound found. */
91     private boolean allowCreate = true;
92  
93     /*** The hibernate query. */
94     private String hql;
95  
96     private String namedQuery;
97  
98     /*** The max rows in ResulSet to doFocus 
99      *  @author Andrej Zachar
100     */
101    private long maxRowsForFocus = Long.MAX_VALUE;
102 
103    /*** The name of object use to get focus property in hibernate sql syntax 
104     * SELECT defaultFocusPropertyObjectAlias.getFocusProperty ...	
105     * 
106     * @author Andrej Zachar
107     */
108    private String defaultFocusPropertyObjectAlias = "";
109 
110    /***
111     * Enable or Disable String length checking of given filters values. If
112     * filter value is null or empty is removed from query.
113     * @author Andrej Zachar
114     */
115    private boolean _isRemoveEmptyStrings = false;
116 
117    private StatementBuilder statementBuilder;
118 
119    /***
120     * Enable or disable optimalization of the query for focus property.
121     */
122    private boolean _focusOptimalization = true;
123 
124    /***
125     * @return Returns the focusOptimalization.
126     */
127    public boolean isFocusOptimalization()
128    {
129       return _focusOptimalization;
130    }
131 
132    /***
133     * Enable or disable optimalization of the query for focus property.
134     * @param focusOptimalization true - enable query with short select, false - query with full select
135     */
136    public void setFocusOptimalization(boolean focusOptimalization)
137    {
138       _focusOptimalization = focusOptimalization;
139    }
140 
141    /***
142     * <p>
143     * If is set, it use special ScrollableResultsDecorator, that enable or
144     * disable to add object in final list.
145     * </p>
146     * <h4>NOTE:</h4>
147     * <p>
148     * Also, it respects the total count of entries that overlap your paged
149     * list.
150     * </p>
151     * @param validator The validator to set.
152     */
153    public void setValidator(ObjectValidator validator)
154    {
155       _validator = validator;
156    }
157 
158    /***
159     * @return Returns the isPrefilterEmpty.
160     */
161    public boolean isRemoveEmptyStrings()
162    {
163       return _isRemoveEmptyStrings;
164    }
165 
166    /***
167     * Enable or Disable String length checking of given filters values. If
168     * filter value is null or empty is removed from query.
169     * 
170     * @param isPrefilterEmpty
171     *            true-remove null and empty, false - remove only null filters.
172     */
173    public void setRemoveEmptyStrings(boolean isPrefilterEmpty)
174    {
175       _isRemoveEmptyStrings = isPrefilterEmpty;
176    }
177 
178    /***
179     * @see net.mlw.vlh.ValueListAdapter#getValueList(java.lang.String,
180     *      net.mlw.vlh.ValueListInfo)
181     */
182    public ValueList getValueList(String name, ValueListInfo info)
183    {
184 
185       LOGGER.debug("getValueList(String, ValueListInfo) - start");
186 
187       if (info.getSortingColumn() == null)
188       {
189          info.setPrimarySortColumn(getDefaultSortColumn());
190          info.setPrimarySortDirection(getDefaultSortDirectionInteger());
191          if (LOGGER.isDebugEnabled())
192          {
193             LOGGER.debug("The default sort column '" + getDefaultSortColumn() + "' with direction '" + getDefaultSortDirectionInteger()
194                   + "' was  set.");
195          }
196       }
197 
198       int numberPerPage = info.getPagingNumberPer();
199 
200       if (numberPerPage == Integer.MAX_VALUE)
201       {
202          numberPerPage = getDefaultNumberPerPage();
203          info.setPagingNumberPer(numberPerPage);
204          if (LOGGER.isDebugEnabled())
205          {
206             LOGGER.debug("The paging number per page '" + numberPerPage + "' was  set.");
207          }
208       }
209 
210       Session session = SessionFactoryUtils.getSession(getSessionFactory(), allowCreate);
211       try
212       {
213          Query query;
214 
215          boolean doFocus = ((getAdapterType() & DO_FOCUS) == 0) && info.isFocusEnabled() && info.isDoFocus() && (namedQuery == null);
216 
217          if (doFocus)
218          {
219             if (LOGGER.isDebugEnabled())
220             {
221                LOGGER.debug("Start to focusing adapterName '" + name + "', ValueListInfo info = " + info + "'");
222             }
223             ScrollableResults results = getScrollableResults(getQueryForFocus(info, session), info);
224             results.beforeFirst();
225             doFocusFor(info, results);
226 
227             if (LOGGER.isDebugEnabled())
228             {
229                LOGGER.debug("Focusing finished for adapterName '" + name + "', ValueListInfo info '" + info + "'");
230             }
231          }
232 
233          query = getQuery(info, session);
234 
235          boolean doPaging = ((getAdapterType() & DO_PAGE) == 0);
236 
237          List list;
238 
239          if (doPaging)
240          {
241             if (LOGGER.isDebugEnabled())
242             {
243                LOGGER.debug("getValueList(String adapterName = " + name + ", ValueListInfo info = " + info
244                      + ") - Start to paging result set");
245             }
246 
247             list = new ArrayList(numberPerPage);
248             ScrollableResults results = getScrollableResults(query, info);
249 
250             results.last();
251             int lastRowNumber = results.getRowNumber();
252             info.setTotalNumberOfEntries(lastRowNumber + 1);
253 
254             if (numberPerPage == 0)
255             {
256                numberPerPage = getDefaultNumberPerPage();
257             }
258 
259             int pageNumber = info.getPagingPage();
260             boolean isResult;
261             if (pageNumber > 1)
262             {
263                if ((pageNumber - 1) * numberPerPage > lastRowNumber)
264                {
265                   pageNumber = (lastRowNumber / numberPerPage) + 1;
266                   info.setPagingPage(pageNumber);
267                }
268             }
269             if (pageNumber > 1)
270             {
271                isResult = results.scroll((pageNumber - 1) * numberPerPage - lastRowNumber);
272             }
273             else
274             {
275                isResult = results.first();
276             }
277 
278             for (int i = 0; i < numberPerPage && isResult; i++)
279             {
280                list.add(results.get(0));
281                isResult = results.next();
282             }
283 
284             LOGGER.debug("Sorting finished.");
285 
286          }
287          else
288          {
289 
290             LOGGER.debug("Retrieving a list directly from the query.");
291 
292             list = query.list();
293             info.setTotalNumberOfEntries(list.size());
294          }
295 
296          ValueList returnValueList = getListBackedValueList(info, list);
297          if (LOGGER.isDebugEnabled())
298          {
299             LOGGER.debug("Retrieved list was wrapped in valuelist, info=" + info);
300          }
301          return returnValueList;
302       }
303       catch (HibernateException e)
304       {
305          LOGGER.error("Error getting data in adapater '" + name + "' with info = '" + info + "'", e);
306          throw SessionFactoryUtils.convertHibernateAccessException(e);
307       }
308       catch (Exception e)
309       {
310          LOGGER.fatal("Fatal error getting data in adapater '" + name + "' with info = '" + info + "'", e);
311          return null;
312       }
313       finally
314       {
315          SessionFactoryUtils.releaseSession(session, getSessionFactory());
316       }
317    }
318 
319    /***     
320     * @param info
321     * @param list
322     * @return DefaultListBackValueList instance
323     */
324    protected ValueList getListBackedValueList(ValueListInfo info, List list)
325    {
326       return new DefaultListBackedValueList(list, info);
327    }
328 
329    /***
330     * @param info
331     * @param results
332     * @throws HibernateException
333     * @throws NoSuchMethodException
334     * @throws InvocationTargetException
335     * @throws IllegalAccessException
336     */
337    private void doFocusFor(ValueListInfo info, ScrollableResults results) throws HibernateException
338    {
339       info.setFocusStatus(ValueListInfo.FOCUS_NOT_FOUND);
340 
341       int currentRow;
342       if (isFocusOptimalization())
343       {
344          if (LOGGER.isDebugEnabled())
345          {
346             LOGGER.debug("Focusing only property '" + info.getFocusProperty() + "' == '" + info.getFocusValue() + "'.");
347          }
348          for (currentRow = 0; ((results.next()) && (currentRow < maxRowsForFocus)); currentRow++)
349          {
350             String value = results.get(0).toString();
351             if (value.equalsIgnoreCase(info.getFocusValue()))
352             {
353                if (LOGGER.isInfoEnabled())
354                {
355                   LOGGER.info("Focus property '" + info.getFocusProperty() + "' in row '" + currentRow + "'.");
356                }
357                info.setPagingPageFromRowNumber(results.getRowNumber());
358                info.setFocusedRowNumberInTable(results.getRowNumber());
359                info.setFocusStatus(ValueListInfo.FOCUS_FOUND);
360                break;
361             }
362          }
363       }
364       else
365       {
366          if (LOGGER.isDebugEnabled())
367          {
368             LOGGER.debug("Focusing object with the property '" + info.getFocusProperty() + "' == '" + info.getFocusValue() + "'.");
369          }
370          for (currentRow = 0; ((results.next()) && (currentRow < maxRowsForFocus)); currentRow++)
371          {
372 
373             Object value;
374             try
375             {
376                value = PropertyUtils.getProperty(results.get(0), info.getFocusProperty());
377             }
378             catch (HibernateException e)
379             {
380                LOGGER.error("Error getting focus property '" + info.getFocusProperty() + "'", e);
381                throw e;
382             }
383             catch (Exception e)
384             {
385                LOGGER.warn("Ingoring error while getting focus property '" + info.getFocusProperty() + "'", e);
386                continue;
387             }
388 
389             if (value.toString().equalsIgnoreCase(info.getFocusValue()))
390             {
391                if (LOGGER.isInfoEnabled())
392                {
393                   LOGGER.info("Focus object's property '" + info.getFocusProperty() + "' was found in the row '" + currentRow + "'.");
394                }
395                info.setPagingPageFromRowNumber(results.getRowNumber());
396                info.setFocusedRowNumberInTable(results.getRowNumber());
397                info.setFocusStatus(ValueListInfo.FOCUS_FOUND);
398                break;
399             }
400          }
401       }
402       if (currentRow == maxRowsForFocus)
403       {
404          if (LOGGER.isInfoEnabled())
405          {
406             LOGGER.info("Focus for property '" + info.getFocusProperty() + "' exceded maximum rows for focus '" + maxRowsForFocus + "'.");
407          }
408          info.setFocusStatus(ValueListInfo.FOCUS_TOO_MANY_ITEMS);
409       }
410    }
411 
412    /***
413     * @param query
414     * @param info ValueListInfo This info will be set to validator. 
415     * @return ScrollableResults, if is set non null _validator, it returns the
416     *         ScrollableResultsDecorator.
417     * @throws HibernateException
418     */
419    private ScrollableResults getScrollableResults(Query query, ValueListInfo info) throws HibernateException
420    {
421       ScrollableResults results;
422 
423       if (_validator == null)
424       {
425          LOGGER.debug("Validator is null, using normal ScrollableResults");
426          results = query.scroll();
427 
428       }
429       else
430       {
431          LOGGER.info("Using decorator of the ScrollableResults with your validator.");
432          _validator.setValueListInfo(info);
433          results = new ScrollableResultsDecorator(query.scroll(), _validator);
434       }
435 
436       return results;
437    }
438 
439    /***
440     * @param info
441     * @param session
442     * @return @throws
443     *         HibernateException
444     */
445    private Query getQuery(ValueListInfo info, Session session) throws HibernateException, ParseException
446    {
447 
448       if (getHql() != null)
449       {
450          return getStatementBuilder().generate(session, new StringBuffer(getHql()), info.getFilters(), _isRemoveEmptyStrings);
451       }
452       else
453       {
454          if (namedQuery != null)
455          {
456             return session.getNamedQuery(getNamedQuery());
457          }
458          else
459          {
460             throw new HibernateException("Please define any QUERY in value list retrieve adpater!");
461          }
462       }
463    }
464 
465    /***
466     * If focus optimalization is true, it select only focus property. For
467     * validator is recommended to set it to false, while you want to validate
468     * properties of retrieved objects.
469     * 
470     * @param info
471     * @param session
472     * @return
473     * @throws HibernateException
474     */
475    private Query getQueryForFocus(ValueListInfo info, Session session) throws HibernateException, ParseException
476    {
477       if (isFocusOptimalization())
478       {
479          LOGGER.info("Focus will use optimalizated query.");
480          return getOptimizedQuery(info, session);
481       }
482       else
483       {
484          LOGGER.info("Focus will use normal (full) query.");
485          return getQuery(info, session);
486       }
487    }
488 
489    /***
490     * 
491     * @param info
492     * @param session
493     * @return query that select only focus property.
494     * @throws HibernateException
495     * @throws ParseException
496     */
497    private Query getOptimizedQuery(ValueListInfo info, Session session) throws HibernateException, ParseException
498    {
499       if (getHql() != null)
500       {
501          return getStatementBuilder().generateForFocus(session, new StringBuffer(getHql()), info.getFilters(), _isRemoveEmptyStrings,
502                defaultFocusPropertyObjectAlias, info.getFocusProperty());
503 
504       }
505       else
506       {
507          throw new HibernateException(
508                "Please define any HQL QUERY in value list retrieve adpater, function is not implemented for NamedQuery!");
509       }
510    }
511 
512    /*** Set the Hibernate SessionFactory to be used by this DAO.
513     * 
514     * @param sessionFactory The Hibernate SessionFactory to be used by this DAO.
515     */
516    public final void setSessionFactory(SessionFactory sessionFactory)
517    {
518       this.sessionFactory = sessionFactory;
519    }
520 
521    /*** Return the Hibernate SessionFactory used by this DAO.
522     * 
523     * @return The Hibernate SessionFactory used by this DAO.
524     */
525    protected final SessionFactory getSessionFactory()
526    {
527       return this.sessionFactory;
528    }
529 
530    /*** Sets the hql used to retrieve the data.
531     * @param hql The hql to set.
532     * @deprecated use setHql(String)
533     */
534    public void setHsql(String hql)
535    {
536       this.hql = hql;
537    }
538 
539    /*** Sets the hql used to retrieve the data.
540     * @param hql The hql to set.
541     */
542    public void setHql(String hql)
543    {
544       this.hql = hql;
545    }
546 
547    /*** Returns the namedQuery.
548     * @return Returns the namedQuery.
549     */
550    public String getNamedQuery()
551    {
552       return namedQuery;
553    }
554 
555    /*** Sets the namedQuery.  
556     * <p>
557     *   NOTE: by using this you can not enable sorting, filtering,
558     *   paging of the data, and  focusing of rows.
559     * </p>
560     * @param namedQuery
561     *            The namedQuery to set.
562     */
563    public void setNamedQuery(String namedQuery)
564    {
565       this.namedQuery = namedQuery;
566    }
567 
568    /*** Gets the hql used to retrieve the data.
569     * @return Returns the hql.
570     */
571    public String getHql()
572    {
573       return hql;
574    }
575 
576    /*** Sets: If a new Session should be created if no thread-bound found.
577     * 
578     * @param allowCreate
579     *            The allowCreate to set.
580     */
581    public void setAllowCreate(boolean allowCreate)
582    {
583       this.allowCreate = allowCreate;
584    }
585 
586    /***
587     * Maximum rows to search with Focus
588     * 
589     * @return Returns the maxRowsForFocus.
590     */
591    public long getMaxRowsForFocus()
592    {
593       return maxRowsForFocus;
594    }
595 
596    /***
597     * Maximum rows to search with Focus
598     * 
599     * @param maxRowsForFocus
600     *            The maxRowsForFocus to set.
601     */
602    public void setMaxRowsForFocus(long maxRowsForFocus)
603    {
604       this.maxRowsForFocus = maxRowsForFocus;
605    }
606 
607    /***
608     * @return Returns the defaultFocusPropertyObject.
609     */
610    public String getDefaultFocusPropertyObjectAlias()
611    {
612       return defaultFocusPropertyObjectAlias;
613    }
614 
615    /*** 
616     * The name of object use to get focus property in hibernate hql syntax 
617     * SELECT defaultFocusPropertyObjectAlias.getFocusProperty ...  
618     * 
619     * @param defaultFocusPropertyObjectAlias
620     *            The defaultFocusPropertyObjectAlias to set.
621     */
622    public void setDefaultFocusPropertyObjectAlias(String defaultFocusPropertyObjectAlias)
623    {
624       this.defaultFocusPropertyObjectAlias = defaultFocusPropertyObjectAlias + ".";
625    }
626 
627    /***
628     * @return Returns the statementBuilder.
629     */
630    public StatementBuilder getStatementBuilder()
631    {
632       if (statementBuilder == null)
633       {
634          statementBuilder = new StatementBuilder();
635       }
636       return statementBuilder;
637    }
638 
639    /***
640     * @param statementBuilder The statementBuilder to set.
641     */
642    public void setStatementBuilder(StatementBuilder statementBuilder)
643    {
644       this.statementBuilder = statementBuilder;
645    }
646 }