001 // Copyright 2008 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.tynamo.jpa.internal;
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.Expression;
024 import javax.persistence.criteria.Root;
025
026 import org.apache.tapestry5.grid.GridDataSource;
027 import org.apache.tapestry5.grid.SortConstraint;
028 import org.apache.tapestry5.ioc.internal.util.Defense;
029
030 /**
031 * A simple implementation of {@link org.apache.tapestry5.grid.GridDataSource} based on a Hibernate
032 * Session and a known entity class. This implementation does support multiple
033 * {@link org.apache.tapestry5.grid.SortConstraint sort constraints}; however it assumes a direct
034 * mapping from sort constraint property to Hibernate property.
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 * @param <E>
042 */
043 public class JPAGridDataSource<E> implements GridDataSource
044 {
045 protected final EntityManager entityManager;
046
047 private final Class<E> entityType;
048
049 private int startIndex;
050
051 private List preparedResults;
052
053 public JPAGridDataSource(EntityManager em, Class<E> entityType)
054 {
055 Defense.notNull(em, "entityManager");
056 Defense.notNull(entityType, "entityType");
057
058 this.entityManager = em;
059 this.entityType = entityType;
060 }
061
062 /**
063 * Returns the total number of rows for the configured entity type.
064 */
065 public int getAvailableRows()
066 {
067 CriteriaBuilder cb = entityManager.getCriteriaBuilder();
068 CriteriaQuery<Long> resultQuery = cb.createQuery(Long.class);
069 Root<E> all = resultQuery.from(entityType);
070 Expression<Boolean> query;
071
072 query = additionalConstraints(cb, all);
073
074 if (query != null)
075 {
076 resultQuery.where(query).select(cb.count(all));
077 }
078 else
079 {
080 resultQuery.select(cb.count(all));
081 }
082
083 TypedQuery<Long> finalQ = entityManager.createQuery(resultQuery);
084 Long result = finalQ.getSingleResult();
085
086 return result.intValue();
087 }
088
089 /**
090 * Prepares the results, performing a query (applying the sort results, and the provided start
091 * and end index). The results can later be obtained from {@link #getRowValue(int)} .
092 *
093 * @param startIndex
094 * index, from zero, of the first item to be retrieved
095 * @param endIndex
096 * index, from zero, of the last item to be retrieved
097 * @param sortConstraints
098 * zero or more constraints used to set the order of the returned values
099 */
100 public void prepare(int startIndex, int endIndex, List<SortConstraint> sortConstraints)
101 {
102 Defense.notNull(sortConstraints, "sortConstraints");
103
104 // We just assume that the property names in the SortContraint match the Hibernate
105 // properties.
106
107 CriteriaBuilder cb = entityManager.getCriteriaBuilder();
108 CriteriaQuery<E> resultQuery = cb.createQuery(entityType);
109 Root<E> all = resultQuery.from(entityType);
110 Expression<Boolean> query;
111
112 query = additionalConstraints(cb,all);
113
114 if (query != null)
115 {
116 resultQuery.where(query).select(all);
117 }
118 else
119 {
120 resultQuery.select(all);
121 }
122
123 for (SortConstraint constraint : sortConstraints)
124 {
125
126 String propertyName = constraint.getPropertyModel().getPropertyName();
127
128 switch (constraint.getColumnSort())
129 {
130
131 case ASCENDING:
132
133 resultQuery.orderBy(cb.asc(all.get(propertyName)));
134 break;
135
136 case DESCENDING:
137 resultQuery.orderBy(cb.desc(all.get(propertyName)));
138 break;
139
140 default:
141 }
142 }
143
144 TypedQuery<E> finalQ = entityManager.createQuery(resultQuery);
145
146 finalQ.setFirstResult(startIndex);
147 finalQ.setMaxResults(endIndex - startIndex + 1);
148
149 this.startIndex = startIndex;
150
151 preparedResults = finalQ.getResultList();
152 }
153
154 /**
155 * Invoked after the main criteria has been set up (firstResult, maxResults and any sort
156 * contraints). This gives subclasses a chance to apply additional constraints before the list
157 * of results is obtained from the criteria. This implementation does nothing and may be
158 * overridden.
159 */
160 protected Expression<Boolean> additionalConstraints(CriteriaBuilder cb, Root<E> root)
161 {
162 return null;
163 }
164
165 /**
166 * Returns a row value at the given index (which must be within the range defined by the call to
167 * {@link #prepare(int, int, java.util.List)} ).
168 *
169 * @param index
170 * of object
171 * @return object at that index
172 */
173 public Object getRowValue(int index)
174 {
175 return preparedResults.get(index - startIndex);
176 }
177
178 /**
179 * Returns the entity type, as provided via the constructor.
180 */
181 public Class getRowType()
182 {
183 return entityType;
184 }
185 }