Differences between revisions 5 and 6
Revision 5 as of 2008-06-17 16:59:36
Size: 8793
Editor: PeterMaloney
Comment: fixed a bad bug with the row index in getRowValue(int)
Revision 6 as of 2009-09-20 23:20:50
Size: 8793
Editor: localhost
Comment: converted to 1.6 markup
No differences found!

Grid datasource for hibernate queries. Abstract methods are used for creating a Query Object, so it is quite flexible; there is no requirement to use a DAO, HQL, or Criteria. It is compatible with any org.hibernate.Query object.

It was originally designed for use with HQL. It probably isn't as optimal for DaO as something specifically designed for that (unless your DaO object returns a org.hibernate.Query object).

To use it, extend it and implement createQuery(...) and createCountQuery(). Optionally use generateOrderBy(...) inside your createQuery(...) method to generate an ORDER BY clause supporting any weird property mapping.

It is intended to be extended for each page/component where it is used, and a new instance is created for each render. Theoretically, you could just call initAvailableRows() instead of creating a new one, or just remove the "if (this.rowCount == -1)" condition in getAvailableRows().

   1 import java.util.ArrayList;
   2 import java.util.List;
   3 import java.util.Map;
   4 
   5 import org.apache.log4j.Category;
   6 import org.apache.tapestry.grid.ColumnSort;
   7 import org.apache.tapestry.grid.GridDataSource;
   8 import org.apache.tapestry.grid.SortConstraint;
   9 import org.hibernate.Query;
  10 
  11 /**
  12  * Grid datasource for hibernate queries. Abstract methods are used for creating
  13  * a Query Object, so it is quite flexible; there is no requirement to use a
  14  * DAO, HQL, or Criteria. It is compatible with any org.hibernate.Query object.<br>
  15  * <br>
  16  * It was originally designed for use with HQL. It probably isn't as optimal for
  17  * DaO as something specifically designed for that (unless your DaO object
  18  * returns a org.hibernate.Query object). <br>
  19  * <br>
  20  * To use it, extend it and implement createQuery(...) and createCountQuery().
  21  * Optionally use generateOrderBy(...) inside your createQuery(...) method to
  22  * generate an ORDER BY clause supporting any weird property mapping. <br>
  23  * <br>
  24  * It is intended to be extended for each page/component where it is used, and a
  25  * new instance is created for each render. Theoretically, you could just call
  26  * initAvailableRows() instead of creating a new one, or just remove the
  27  * &quot;if (this.rowCount == -1)&quot; condition in getAvailableRows().<br>
  28  * <br>
  29  * Tested with Tapestry 5.0.11, JDK1.6
  30  * 
  31  * @author pmaloney, tli
  32  * @version $Id: HibernateGridDataSource.java,v 1.6 2008/06/10 17:37:07 pmaloney
  33  *          Exp $
  34  */
  35 public abstract class HibernateGridDataSource implements GridDataSource {
  36     private static Category log = Category.getInstance( HibernateGridDataSource.class );
  37 
  38     private int rowCount = -1;
  39     private int startIndex = -1;
  40     private List<?> pageRowList;
  41 
  42     private Class<?> rowType;
  43 
  44 
  45     public HibernateGridDataSource(Class<?> rowType) {
  46         this.rowType = rowType;
  47         this.rowCount = -1;
  48     }
  49 
  50     /**
  51      * @return a Query for fetching rows for a page. There should be no OFFSET
  52      *         or LIMIT clause, since setFirstResult(...) and setFetchSize(...)
  53      *         will be called by HibernateGridDataSource. Call
  54      *         HibernateGridDataSource.generateOrderBy(List,Map) to generate an
  55      *         order by clause.
  56      */
  57     public abstract Query createQuery(List<SortConstraint> sortConstraints);
  58 
  59     /**
  60      * @return a Query that would be suitable for finding a count. Its result
  61      *         should be one java.lang.Number (Such as Integer, or Long) (one
  62      *         row, one cell).
  63      */
  64     public abstract Query createCountQuery();
  65     
  66     /**
  67      * @param sortConstraints The List of SortConstraint objects to sort with
  68      * @param propertyToHql maps property names from the grid bean model to hql
  69      *        expressions. If the map is not null and the property name is in
  70      *        the map, the expression is used, else the property name is used
  71      *        normally.
  72      */
  73     public static String generateOrderBy(List<SortConstraint> sortConstraints, Map<String,String> propertyToHql) {
  74         StringBuilder hql = new StringBuilder();
  75         ArrayList<String> orderByExpressions = new ArrayList<String>();
  76         
  77         for( SortConstraint sc : sortConstraints ) {
  78             if( sc.getColumnSort() == ColumnSort.UNSORTED )
  79                 continue;
  80             
  81             String name = sc.getPropertyModel().getPropertyName();
  82             
  83             if( propertyToHql != null ) {
  84                 String hqlEx = propertyToHql.get(name);
  85                 if( hqlEx != null ) {
  86                     name = hqlEx;
  87                 }
  88             }
  89             
  90             String ex;
  91             if( sc.getColumnSort() == ColumnSort.DESCENDING )
  92                 ex = name + " desc";
  93             else
  94                 ex = name;
  95             
  96             orderByExpressions.add( ex );
  97         }
  98         
  99         int i=0;
 100         for( String ex : orderByExpressions ) {
 101             if( i == 0 )
 102                 hql.append(" ORDER BY ");
 103             else
 104                 hql.append(", ");
 105             hql.append( ex );
 106             i++;
 107         }
 108 
 109         if( log.isDebugEnabled() )
 110             log.debug( "In generateOrderBy(...), hql = " + hql );
 111         return hql.toString();
 112     }
 113     
 114     /**
 115      * Calls generateOrderBy(sortConstraints, null)
 116      * 
 117      * @See generateOrderBy(List<SortConstraint>, Map<String,String>)
 118      */
 119     public static String generateOrderBy(List<SortConstraint> sortConstraints) {
 120         return generateOrderBy(sortConstraints, null);
 121     }
 122     
 123     public void initAvailableRows(){
 124         Query q = createCountQuery();
 125         Number count = (Number) q.uniqueResult();
 126         
 127         if( log.isDebugEnabled() )
 128             log.debug("count = " + count);
 129         
 130         this.rowCount = count.intValue();
 131     }
 132     
 133     /**
 134      * Returns the number of rows available in the data source.
 135      */
 136     public int getAvailableRows() {
 137         if (this.rowCount == -1)
 138             initAvailableRows();        
 139         return rowCount;
 140     }
 141 
 142     /**
 143      * Invoked to allow the source to prepare to present values. This gives the
 144      * source a chance to pre-fetch data (when appropriate) and informs the
 145      * source of the desired sort order.
 146      * 
 147      * @param startIndex
 148      *            the starting index to be retrieved
 149      * @param endIndex
 150      *            the ending index to be retrieved
 151      * @param sortConstraints
 152      *            the property model that defines what data will be used for
 153      *            sorting, or null if no sorting is required (in which case,
 154      *            whatever natural order is provided by the underlying data
 155      *            source will be used)
 156      */
 157     public void prepare(int startIndex, int endIndex, List<SortConstraint> sortConstraints) {
 158         if( log.isDebugEnabled() )
 159             log.debug(
 160                 "Processing prepare(...) startIndex = " + startIndex + ", endIndex = " + endIndex );     
 161         int maxResults = endIndex - startIndex + 1;
 162         
 163         Query q = createQuery(sortConstraints);
 164         q.setFirstResult(startIndex);
 165         q.setFetchSize(maxResults);
 166         q.setMaxResults(maxResults);
 167         List<?> rows = q.list();
 168         pageRowList = rows;  
 169         this.startIndex = startIndex;
 170     }
 171 
 172     /**
 173      * Returns the row value as rows on evt the provided index. This method will
 174      * be invoked in sequential order.
 175      * 
 176      * @param databaseIndex index based on all rows (superset of what was
 177      *        fetched in prepare), so subtract startIndex to get the index in
 178      *        the result set.
 179      */
 180     public Object getRowValue(int databaseIndex) {
 181         int collectionIndex = databaseIndex - startIndex;
 182         Object entityObject = null;
 183         
 184         if (pageRowList.size() > collectionIndex)
 185             entityObject = pageRowList.get(collectionIndex);
 186         else {
 187             // This else is a work around for getRowValue(...) with incorrect index.
 188             // The theory is that the grid is caching the number of rows so if the row count
 189             // on the previous request was larger, then index can be out of bounds.
 190             try {
 191                 return getRowType().newInstance(); 
 192             } catch (Exception e) {
 193                 throw new RuntimeException("index was invalid, and failed to create a dummy row.", e);
 194             } 
 195         }
 196         return entityObject;
 197     }
 198 
 199     /**
 200      * Returns the type of value in the rows, or null if not known.
 201      * 
 202      * @return the row type, or null
 203      */
 204     public Class<?> getRowType() {
 205         return this.rowType;
 206     }
 207    
 208 }

Tapestry5HibernateGridDatasource3 (last edited 2009-09-20 23:20:50 by localhost)