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