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}