001 // Copyright 2007-2008 Thiago H. de Paula Figueiredo
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 br.com.arsmachina.dao.hibernate;
016
017 import java.io.Serializable;
018 import java.util.List;
019
020 import org.hibernate.Criteria;
021 import org.hibernate.LockMode;
022 import org.hibernate.SessionFactory;
023 import org.hibernate.classic.Session;
024 import org.hibernate.criterion.Example;
025 import org.hibernate.criterion.MatchMode;
026 import org.hibernate.criterion.Order;
027 import org.hibernate.criterion.Projections;
028 import org.hibernate.criterion.Restrictions;
029
030 import br.com.arsmachina.dao.ReadableDAO;
031 import br.com.arsmachina.dao.SortCriterion;
032
033 /**
034 * {@link ReadableDAO} implementation using Hibernate. All methods use {@link #getSession()} to get
035 * {@link Session}.
036 *
037 * @author Thiago H. de Paula Figueiredo
038 * @param <T> the entity class related to this DAO.
039 * @param <K> the type of the field that represents the entity class' primary key.
040 */
041 public abstract class ReadableDAOImpl<T, K extends Serializable> extends BaseHibernateDAO<T, K>
042 implements ReadableDAO<T, K> {
043
044 /**
045 * A {@link SortCriterion} array with no elements.
046 */
047 final public static SortCriterion[] EMPTY_SORTING_CRITERIA = new SortCriterion[0];
048
049 final private String defaultHqlOrderBy = toHqlOrderBy(getDefaultSortCriteria());
050
051 /**
052 * Returns a HQL <code>order by</code> clause given some {@link SortCriterion}s.
053 *
054 * @param sortCriteria {@link SortCriterion} instances.
055 * @return a {@link String}.
056 */
057 final public static String toHqlOrderBy(SortCriterion... sortCriteria) {
058
059 String string = "";
060
061 if (sortCriteria.length > 0) {
062
063 StringBuilder builder = new StringBuilder(" ORDER BY ");
064
065 for (int i = 0; i < sortCriteria.length - 1; i++) {
066 builder.append(sortCriteria.toString());
067 builder.append(", ");
068 }
069
070 builder.append(sortCriteria[sortCriteria.length - 1]);
071
072 string = builder.toString();
073
074 }
075
076 return string;
077
078 }
079
080 /**
081 * Constructor that takes a {@link Class} and a {@link SessionFactory}.
082 *
083 * @param clasz a {@link Class}.
084 * @param sessionFactory a {@link SessionFactory}. It cannot be null.
085 */
086 @SuppressWarnings("unchecked")
087 public ReadableDAOImpl(SessionFactory sessionFactory) {
088 super(null, sessionFactory);
089 }
090
091 /**
092 * Constructor that takes a {@link Class} and a {@link SessionFactory}.
093 *
094 * @param clasz a {@link Class}.
095 * @param sessionFactory a {@link SessionFactory}. It cannot be null.
096 */
097 @SuppressWarnings("unchecked")
098 public ReadableDAOImpl(Class<T> clasz, SessionFactory sessionFactory) {
099 super(clasz, sessionFactory);
100 }
101
102 /**
103 * @see br.com.arsmachina.dao.ReadableDAO#countAll()
104 */
105 public int countAll() {
106
107 final Criteria criteria = createCriteria();
108
109 criteria.setProjection(Projections.rowCount());
110
111 return (Integer) criteria.uniqueResult();
112
113 }
114
115 /**
116 * Returns all the entity class' objects. They are sorted according to
117 * {@link #getDefaultSortCriterions()}.
118 *
119 * @see br.com.arsmachina.dao.ReadableDAO#findAll()
120 * @see #getDefaultSortCriterions()
121 */
122 @SuppressWarnings("unchecked")
123 public List<T> findAll() {
124
125 Criteria criteria = createCriteria();
126 addSortCriteria(criteria, getDefaultSortCriteria());
127 return criteria.list();
128
129 }
130
131 /**
132 * @see br.com.arsmachina.dao.ReadableDAO#findById(java.io.Serializable)
133 */
134 @SuppressWarnings("unchecked")
135 public T findById(K id) {
136 return (T) getSession().get(getEntityClass(), id);
137 }
138
139 /**
140 * @see br.com.arsmachina.dao.ReadableDAO#findByIds(K[])
141 */
142 @SuppressWarnings("unchecked")
143 public List<T> findByIds(K... ids) {
144
145 Criteria criteria = createCriteria();
146 criteria.add(Restrictions.in(getPrimaryKeyPropertyName(), ids));
147 return criteria.list();
148
149 }
150
151 /**
152 * @see br.com.arsmachina.dao.ReadableDAO#findByExample(java.lang.Object)
153 */
154 @SuppressWarnings("unchecked")
155 public List<T> findByExample(T example) {
156
157 Criteria criteria = createCriteria();
158
159 if (example != null) {
160 criteria.add(createExample(example));
161 }
162
163 return criteria.list();
164
165 }
166
167 /**
168 * @see br.com.arsmachina.dao.WriteableDAO#refresh(java.lang.Object)
169 */
170 public void refresh(T object) {
171 getSession().refresh(object);
172 }
173
174 /**
175 * If <code>sortingConstraints</code> is <code>null</code> or empty, this implementation
176 * sort the results by the {@link SortCriterion}s returned by
177 * {@link #getDefaultSortCriterions()}.
178 *
179 * @see br.com.arsmachina.dao.ReadableDAO#findAll(int, int,
180 * br.com.arsmachina.dao.SortCriterion[])
181 */
182 @SuppressWarnings("unchecked")
183 public List<T> findAll(int firstResult, int maximumResults, SortCriterion... sortingConstraints) {
184
185 Criteria criteria = createCriteria();
186 criteria.setFirstResult(firstResult);
187 criteria.setMaxResults(maximumResults);
188
189 if (sortingConstraints == null || sortingConstraints.length == 0) {
190 sortingConstraints = getDefaultSortCriteria();
191 }
192
193 addSortCriteria(criteria, sortingConstraints);
194
195 return criteria.list();
196
197 }
198
199 /**
200 * Reattaches the object to the current {@link org.hibernate.Session} using
201 * <code>Session.lock(object, LockMode.NONE)</code> and then returns the object.
202 *
203 * @param a <code>T</code>.
204 * @return <code>object</code>.
205 * @see br.com.arsmachina.dao.ReadableDAO#reattach(java.lang.Object)
206 */
207 public T reattach(T object) {
208
209 getSession().lock(object, LockMode.NONE);
210 return object;
211
212 }
213
214 /**
215 * Adds <code>sortCriteria</code> to a {@link Criteria} instance.
216 *
217 * @param criteria a {@link Criteria}. It cannot be null.
218 * @param sortCriteria a {@link SortCriterion}<code>...</code>. It cannot be null.
219 * @todo Support for property paths, not just property names.
220 */
221 final public void addSortCriteria(Criteria criteria, SortCriterion... sortCriteria) {
222
223 assert criteria != null;
224
225 if (sortCriteria == null || sortCriteria.length == 0) {
226 sortCriteria = getDefaultSortCriteria();
227 }
228
229 for (SortCriterion sortingConstraint : sortCriteria) {
230
231 final String property = sortingConstraint.getProperty();
232 final boolean ascending = sortingConstraint.isAscending();
233 final Order order = ascending ? Order.asc(property) : Order.desc(property);
234 criteria.addOrder(order);
235
236 }
237
238 }
239
240 /**
241 * Adds the default sort criteria to a {@link Criteria} instance. This method just does
242 * <code>addSortCriteria(criteria, getDefaultSortCriteria());</code>.
243 *
244 * @param criteria a {@link Criteria}. It cannot be null.
245 */
246 protected void addSortCriteria(Criteria criteria) {
247 addSortCriteria(criteria, getDefaultSortCriteria());
248 }
249
250 /**
251 * Returns the default {@link SortCriterion}s to be used to sort the objects lists returned by
252 * methods like {@link #findAll()} and {@link #findAll(int, int, SortCriterion...)} when no
253 * sorting constraints are given. This implementation returns {@link #EMPTY_SORTING_CRITERIA}.
254 *
255 * @return a {@link SortCriterion} array. It cannot be <code>null</code>.
256 */
257 public SortCriterion[] getDefaultSortCriteria() {
258 return EMPTY_SORTING_CRITERIA;
259 }
260
261 /**
262 * Creates a {@link Criteria} for this entity class.
263 *
264 * @return a {@link Criteria}.
265 */
266 public Criteria createCriteria() {
267 return getSession().createCriteria(getEntityClass());
268 }
269
270 /**
271 * Creates a {@link Criteria} for this entity class with given sort criteria.
272 *
273 * @return a {@link Criteria}.
274 */
275 public Criteria createCriteria(SortCriterion ... sortCriteria) {
276
277 Criteria criteria = createCriteria();
278 addSortCriteria(criteria, sortCriteria);
279 return criteria;
280
281 }
282
283 /**
284 * Creates a {@link Criteria} for this entity class with given sort criteria,
285 * first result index and maximum number of results.
286 *
287 * @return a {@link Criteria}.
288 */
289 public Criteria createCriteria(int firstIndex, int maximumResults, SortCriterion ... sortCriteria) {
290
291 Criteria criteria = createCriteria(sortCriteria);
292 criteria.setFirstResult(firstIndex);
293 criteria.setMaxResults(maximumResults);
294 return criteria;
295
296 }
297
298 /**
299 * Used by {@link #findByExample(Object)} to create an {@link Example} instance.
300 *
301 * @todo add criteria for property types not handled by Example (primary keys, associations,
302 * etc)
303 * @return an {@link Example}.
304 */
305 public Example createExample(T entity) {
306
307 Example example = Example.create(entity);
308 example.enableLike(MatchMode.ANYWHERE);
309 example.excludeZeroes();
310 example.ignoreCase();
311
312 return example;
313
314 }
315
316 /**
317 * Returns the value of the <code>defaultHqlOrderBy</code> property.
318 *
319 * @return a {@link String}.
320 */
321 public final String getDefaultHqlOrderBy() {
322 return defaultHqlOrderBy;
323 }
324
325 }