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    public int getAvailableRows()
065    {
066        final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
067
068        CriteriaQuery<Long> criteria = builder.createQuery(Long.class);
069
070        final Root<E> root = criteria.from(entityType);
071
072        criteria = criteria.select(builder.count(root));
073
074        applyAdditionalConstraints(criteria, root, builder);
075
076        return entityManager.createQuery(criteria).getSingleResult().intValue();
077    }
078
079    /**
080     * {@inheritDoc}
081     */
082    public void prepare(final int startIndex, final int endIndex,
083            final List<SortConstraint> sortConstraints)
084    {
085        final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
086
087        final CriteriaQuery<E> criteria = builder.createQuery(entityType);
088
089        final Root<E> root = criteria.from(entityType);
090
091        applyAdditionalConstraints(criteria.select(root), root, builder);
092
093        for (final SortConstraint constraint : sortConstraints)
094        {
095
096            final String propertyName = constraint.getPropertyModel().getPropertyName();
097
098            final Path<Object> propertyPath = root.get(propertyName);
099
100            switch (constraint.getColumnSort())
101            {
102
103                case ASCENDING:
104
105                    criteria.orderBy(builder.asc(propertyPath));
106                    break;
107
108                case DESCENDING:
109                    criteria.orderBy(builder.desc(propertyPath));
110                    break;
111
112                default:
113            }
114        }
115
116        final TypedQuery<E> query = entityManager.createQuery(criteria);
117
118        query.setFirstResult(startIndex);
119        query.setMaxResults(endIndex - startIndex + 1);
120
121        this.startIndex = startIndex;
122
123        preparedResults = query.getResultList();
124
125    }
126
127    protected void applyAdditionalConstraints(final CriteriaQuery<?> criteria, final Root<E> root,
128            final CriteriaBuilder builder)
129    {
130    }
131
132    /**
133     * {@inheritDoc}
134     */
135    public Object getRowValue(final int index)
136    {
137        return preparedResults.get(index - startIndex);
138    }
139
140    /**
141     * {@inheritDoc}
142     */
143    public Class<E> getRowType()
144    {
145        return entityType;
146    }
147
148}