    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005//     http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.hibernate;
    014
    015import org.apache.tapestry5.grid.GridDataSource;
    016import org.apache.tapestry5.grid.SortConstraint;
    017import org.hibernate.Criteria;
    018import org.hibernate.Session;
    019import org.hibernate.criterion.Order;
    020import org.hibernate.criterion.Projections;
    021
    022import java.util.List;
    023
    024/**
    025 * A simple implementation of {@link org.apache.tapestry5.grid.GridDataSource} based on a Hibernate Session and a known
    026 * entity class.  This implementation does support multiple {@link org.apache.tapestry5.grid.SortConstraint sort
    027 * constraints}; however it assumes a direct mapping from sort constraint property to Hibernate property.
    028 *
    029 * This class is <em>not</em> thread-safe; it maintains internal state.
    030 *
    031 * Typically, an instance of this object is created fresh as needed (that is, it is not stored between requests).
    032 */
    033public class HibernateGridDataSource implements GridDataSource
    034{
    035    private final Session session;
    036
    037    private final Class entityType;
    038
    039    private int startIndex;
    040
    041    private List preparedResults;
    042
    043    public HibernateGridDataSource(Session session, Class entityType)
    044    {
    045        assert session != null;
    046        assert entityType != null;
    047        this.session = session;
    048        this.entityType = entityType;
    049    }
    050
    051    /**
    052     * Returns the total number of rows for the configured entity type.
    053     */
    054    @Override
    055    public int getAvailableRows()
    056    {
    057        Criteria criteria = session.createCriteria(entityType);
    058
    059        applyAdditionalConstraints(criteria);
    060
    061        criteria.setProjection(Projections.rowCount());
    062
    063        Number result = (Number) criteria.uniqueResult();
    064
    065        return result.intValue();
    066    }
    067
    068    /**
    069     * Prepares the results, performing a query (applying the sort results, and the provided start and end index). The
    070     * results can later be obtained from {@link #getRowValue(int)} }.
    071     *
    072     * @param startIndex      index, from zero, of the first item to be retrieved
    073     * @param endIndex        index, from zero, of the last item to be retrieved
    074     * @param sortConstraints zero or more constraints used to set the order of the returned values
    075     */
    076    @Override
    077    public void prepare(int startIndex, int endIndex, List<SortConstraint> sortConstraints)
    078    {
    079        assert sortConstraints != null;
    080        Criteria crit = session.createCriteria(entityType);
    081
    082        crit.setFirstResult(startIndex).setMaxResults(endIndex - startIndex + 1);
    083
    084        for (SortConstraint constraint : sortConstraints)
    085        {
    086
    087            String propertyName = constraint.getPropertyModel().getPropertyName();
    088
    089            switch (constraint.getColumnSort())
    090            {
    091
    092                case ASCENDING:
    093
    094                    crit.addOrder(Order.asc(propertyName));
    095                    break;
    096
    097                case DESCENDING:
    098                    crit.addOrder(Order.desc(propertyName));
    099                    break;
    100
    101                default:
    102            }
    103        }
    104
    105        applyAdditionalConstraints(crit);
    106
    107        this.startIndex = startIndex;
    108
    109        preparedResults = crit.list();
    110    }
    111
    112    /**
    113     * Invoked after the main criteria has been set up (firstResult, maxResults and any sort contraints). This gives
    114     * subclasses a chance to apply additional constraints before the list of results is obtained from the criteria.
    115     * This implementation does nothing and may be overridden.
    116     */
    117    protected void applyAdditionalConstraints(Criteria crit)
    118    {
    119    }
    120
    121    /**
    122     * Returns a row value at the given index (which must be within the range defined by the call to {@link
    123     * #prepare(int, int, java.util.List)} ).
    124     *
    125     * @param index of object
    126     * @return object at that index
    127     */
    128    @Override
    129    public Object getRowValue(int index)
    130    {
    131        return preparedResults.get(index - startIndex);
    132    }
    133
    134    /**
    135     * Returns the entity type, as provided via the constructor.
    136     */
    137    @Override
    138    public Class getRowType()
    139    {
    140        return entityType;
    141    }
    142}