1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package ch.elca.el4j.services.persistence.jpa.util;
18
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import javax.persistence.EntityManager;
24 import javax.persistence.NoResultException;
25 import javax.persistence.NonUniqueResultException;
26 import javax.persistence.Query;
27
28 import org.hibernate.FetchMode;
29 import org.hibernate.Session;
30 import org.hibernate.criterion.CriteriaSpecification;
31 import org.hibernate.criterion.Criterion;
32 import org.hibernate.criterion.DetachedCriteria;
33 import org.hibernate.criterion.Restrictions;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import ch.elca.el4j.services.persistence.jpa.helper.JpaHelperImpl;
38 import ch.elca.el4j.services.persistence.jpa.util.QueryException;;
39
40
41
42
43
44
45
46
47
48 public class JpaQuery<T> {
49
50
51 private static final Logger s_log
52 = LoggerFactory.getLogger(JpaQuery.class);
53
54
55 public enum Order {
56
57 ASCENDING,
58
59 DESCENDING
60 }
61
62
63 public enum Relation {
64
65 EQ,
66
67 NE,
68
69 LE,
70
71 GE,
72
73 LT,
74
75 GT;
76 };
77
78
79 private Map<String, Object> conditions;
80
81
82 private DetachedCriteria criteria;
83
84
85
86
87
88 private boolean failOnNull;
89
90
91
92
93 private boolean detach;
94
95
96 private Class<T> domainClass;
97
98
99 private EntityManager em;
100
101
102
103
104
105
106
107 public JpaQuery(Class<T> cls, JpaHelperImpl ds) {
108 domainClass = cls;
109 criteria = DetachedCriteria.forClass(cls);
110 criteria.setResultTransformer(
111 CriteriaSpecification.DISTINCT_ROOT_ENTITY);
112 failOnNull = false;
113 conditions = new HashMap<String, Object>();
114 em = ds.getEntityManager();
115 }
116
117
118
119
120
121
122
123 public JpaQuery<T> where(String key, Object value) {
124 if (value == null) {
125 return whereNull(key);
126 }
127 criteria.add(Restrictions.eq(key, value));
128 conditions.put(key, value);
129 return this;
130 }
131
132
133
134
135
136
137 public JpaQuery<T> where(Criterion c) {
138 criteria.add(c);
139 return this;
140 }
141
142
143
144
145
146
147
148
149 public JpaQuery<T> where(String key, Relation r, Object value) {
150 switch (r) {
151 case EQ:
152 criteria.add(Restrictions.eq(key, value));
153 break;
154 case NE:
155 criteria.add(Restrictions.ne(key, value));
156 break;
157 case LE:
158 criteria.add(Restrictions.le(key, value));
159 break;
160 case LT:
161 criteria.add(Restrictions.lt(key, value));
162 break;
163 case GE:
164 criteria.add(Restrictions.ge(key, value));
165 break;
166 case GT:
167 criteria.add(Restrictions.gt(key, value));
168 break;
169 default:
170
171 throw new QueryException("Not yet implemented: " + r);
172 }
173 return this;
174 }
175
176
177
178
179
180
181 public JpaQuery<T> whereNull(String key) {
182 criteria.add(Restrictions.isNull(key));
183 conditions.put(key, "(null)");
184 return this;
185 }
186
187
188
189
190
191
192 public JpaQuery<T> whereNotNull(String key) {
193 criteria.add(Restrictions.isNotNull(key));
194 conditions.put(key, "(not null)");
195 return this;
196 }
197
198
199
200
201
202
203
204 public JpaQuery<T> order(String key, Order order) {
205 if (order == Order.ASCENDING) {
206 criteria.addOrder(org.hibernate.criterion.Order.asc(key));
207 } else {
208 criteria.addOrder(org.hibernate.criterion.Order.desc(key));
209 }
210 return this;
211 }
212
213
214
215
216
217
218 public JpaQuery<T> extent(String... names) {
219 for (String name : names) {
220 criteria.setFetchMode(name, FetchMode.JOIN);
221 }
222 return this;
223 }
224
225
226
227
228
229 public JpaQuery<T> failOnNull() {
230 failOnNull = true;
231 return this;
232 }
233
234
235
236
237
238 public JpaQuery<T> detach() {
239 detach = true;
240 return this;
241 }
242
243
244
245
246
247 public List<T> execute() {
248 Session session = session();
249
250
251
252 @SuppressWarnings("unchecked")
253 List<T> list = criteria.getExecutableCriteria(session).list();
254 if (failOnNull && list.isEmpty()) {
255 throw new NoResultException("Empty list returned, fail on "
256 + "null is set.");
257 }
258
259 if (detach) {
260 for (T element : list) {
261 session.evict(element);
262 }
263 }
264
265 return list;
266 }
267
268
269
270
271
272
273
274 public T executeUnique() {
275 Session session = session();
276
277
278
279 @SuppressWarnings("unchecked")
280 List<T> list = criteria.getExecutableCriteria(session).list();
281
282 if (list.size() == 0) {
283 if (failOnNull) {
284 throw new NoResultException("No element found");
285 } else {
286 return null;
287 }
288 } else if (list.size() == 1) {
289 T element = list.get(0);
290 if (detach) {
291 session.evict(element);
292 }
293 return element;
294 } else {
295 StringBuilder builder = new StringBuilder();
296 builder.append("Multiple matches (");
297 builder.append(list.size());
298 builder.append(") for query: ");
299 builder.append("SELECT FROM ");
300 builder.append(domainClass.getSimpleName());
301 for (String key : conditions.keySet()) {
302 builder.append(" WHERE ");
303 builder.append(key);
304 builder.append(" = '");
305 builder.append(conditions.get(key));
306 builder.append("'");
307 }
308 builder.append(";");
309
310 throw new NonUniqueResultException(builder.toString());
311 }
312 }
313
314
315
316
317
318
319
320
321
322
323
324 @SuppressWarnings({ "rawtypes", "unchecked" })
325 private List<T> checkTypes(List list, String query, Object... params) {
326 for (Object o : list) {
327 if (!domainClass.isAssignableFrom(o.getClass())) {
328 StringBuilder builder = new StringBuilder(
329 "HQL query returned different type than expected. Wanted ");
330 builder.append(domainClass.getName());
331 builder.append(" Got ");
332 builder.append(o.getClass().getName());
333 builder.append(" Query ");
334 builder.append(query);
335 for (Object p : params) {
336 builder.append(", ");
337 builder.append(p);
338 }
339 throw new QueryException(builder.toString());
340 }
341 }
342
343 return (List<T>) list;
344 }
345
346
347
348
349
350
351
352 private Query query(String queryString, Object... params) {
353 Query query = em.createQuery(queryString);
354
355 for (int i = 0; i < params.length; i++) {
356
357
358
359 if (params[i] == null) {
360 throw new QueryException("Found a null parameter in a "
361 + "HQL query. This is almost certainly a mistake.");
362 }
363
364
365 query.setParameter(i + 1, params[i]);
366 }
367 return query;
368 }
369
370
371
372
373
374 private void detach(Object object) {
375
376 Session session = (Session) em.getDelegate();
377 session.evict(object);
378 }
379
380
381
382
383
384
385
386
387
388
389
390 public List<T> executeHQL(String query, Object... params) {
391 Query q = query(query, params);
392 List<T> list = checkTypes(q.getResultList(), query, params);
393
394 if (failOnNull && list.isEmpty()) {
395 throw new NoResultException("Empty list returned, fail on "
396 + "null is set.");
397 }
398
399 if (detach) {
400 for (T element : list) {
401 detach(element);
402 }
403 }
404
405 return list;
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419 public T executeHQLUnique(String query, Object... params) {
420
421 Query q = query(query, params);
422 List<T> list = checkTypes(q.getResultList(), query, params);
423
424 if (list.size() == 0) {
425 if (failOnNull) {
426 throw new NoResultException("No element found");
427 } else {
428 return null;
429 }
430 } else if (list.size() == 1) {
431 T element = list.get(0);
432 if (detach) {
433 detach(element);
434 }
435 return element;
436 } else {
437 StringBuilder builder = new StringBuilder(
438 "Multiple matches for query ");
439 builder.append(query);
440 for (Object p : params) {
441 builder.append(", ");
442 builder.append(p);
443 }
444 throw new NonUniqueResultException(builder.toString());
445 }
446 }
447
448
449
450
451
452 private Session session() {
453
454 Session session = (Session) em.getDelegate();
455 if (!session.isOpen()) {
456
457 session = session.getSessionFactory().openSession();
458 }
459 return session;
460 }
461 }