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    
015    package org.apache.tapestry5.jpa;
016    
017    import java.util.List;
018    
019    import javax.persistence.EntityManager;
020    import javax.persistence.TypedQuery;
021    import javax.persistence.criteria.CriteriaBuilder;
022    import javax.persistence.criteria.CriteriaQuery;
023    import javax.persistence.criteria.Path;
024    import javax.persistence.criteria.Root;
025    
026    import org.apache.tapestry5.grid.GridDataSource;
027    import 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     */
043    public 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    }