    001// Copyright 2011 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.jpa;
    016
    017import java.util.List;
    018
    019import javax.persistence.EntityManager;
    020import javax.persistence.TypedQuery;
    021import javax.persistence.criteria.CriteriaBuilder;
    022import javax.persistence.criteria.CriteriaQuery;
    023import javax.persistence.criteria.Path;
    024import javax.persistence.criteria.Root;
    025
    026import org.apache.tapestry5.grid.GridDataSource;
    027import org.apache.tapestry5.grid.SortConstraint;
    028
    029/**
    030 * A simple implementation of {@link org.apache.tapestry5.grid.GridDataSource} based on a
    031 * {@linkplain javax.persistence.EntityManager} and a known
    032 * entity class. This implementation does support multiple
    033 * {@link org.apache.tapestry5.grid.SortConstraint sort
    034 * constraints}.
    035 * <p/>
    036 * This class is <em>not</em> thread-safe; it maintains internal state.
    037 * <p/>
    038 * Typically, an instance of this object is created fresh as needed (that is, it is not stored
    039 * between requests).
    040 *
    041 * @since 5.3
    042 */
    043public class JpaGridDataSource<E> implements GridDataSource
    044{
    045
    046    private final EntityManager entityManager;
    047
    048    private final Class<E> entityType;
    049
    050    private int startIndex;
    051
    052    private List<E> preparedResults;
    053
    054    public JpaGridDataSource(final EntityManager entityManager, final Class<E> entityType)
    055    {
    056        super();
    057        this.entityManager = entityManager;
    058        this.entityType = entityType;
    059    }
    060
    061    /**
    062     * {@inheritDoc}
    063     */
    064    @Override
    065    public int getAvailableRows()
    066    {
    067        final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    068
    069        CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
    070
    071        final Root<E> root = criteria.from(entityType);
    072
    073        criteria = criteria.select(builder.count(root));
    074
    075        applyAdditionalConstraints(criteria, root, builder);
    076
    077        return entityManager.createQuery(criteria).getSingleResult().intValue();
    078    }
    079
    080    /**
    081     * {@inheritDoc}
    082     */
    083    @Override
    084    public void prepare(final int startIndex, final int endIndex,
    085            final List<SortConstraint> sortConstraints)
    086    {
    087        final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    088
    089        final CriteriaQuery<E> criteria = builder.createQuery(entityType);
    090
    091        final Root<E> root = criteria.from(entityType);
    092
    093        applyAdditionalConstraints(criteria.select(root), root, builder);
    094
    095        for (final SortConstraint constraint : sortConstraints)
    096        {
    097
    098            final String propertyName = constraint.getPropertyModel().getPropertyName();
    099
    100            final Path<Object> propertyPath = root.get(propertyName);
    101
    102            switch (constraint.getColumnSort())
    103            {
    104
    105                case ASCENDING:
    106
    107                    criteria.orderBy(builder.asc(propertyPath));
    108                    break;
    109
    110                case DESCENDING:
    111                    criteria.orderBy(builder.desc(propertyPath));
    112                    break;
    113
    114                default:
    115            }
    116        }
    117
    118        final TypedQuery<E> query = entityManager.createQuery(criteria);
    119
    120        query.setFirstResult(startIndex);
    121        query.setMaxResults(endIndex - startIndex + 1);
    122
    123        this.startIndex = startIndex;
    124
    125        preparedResults = query.getResultList();
    126
    127    }
    128
    129    protected void applyAdditionalConstraints(final CriteriaQuery<?> criteria, final Root<E> root,
    130            final CriteriaBuilder builder)
    131    {
    132    }
    133
    134    /**
    135     * {@inheritDoc}
    136     */
    137    @Override
    138    public Object getRowValue(final int index)
    139    {
    140        return preparedResults.get(index - startIndex);
    141    }
    142
    143    /**
    144     * {@inheritDoc}
    145     */
    146    @Override
    147    public Class<E> getRowType()
    148    {
    149        return entityType;
    150    }
    151
    152}