1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package ch.elca.el4j.services.persistence.hibernate.dao;
19
20 import java.io.Serializable;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.ParameterizedType;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Iterator;
27 import java.util.List;
28
29 import org.apache.commons.collections.map.ReferenceMap;
30 import org.hibernate.Criteria;
31 import org.hibernate.EntityMode;
32 import org.hibernate.FetchMode;
33 import org.hibernate.Hibernate;
34 import org.hibernate.HibernateException;
35 import org.hibernate.LockMode;
36 import org.hibernate.SessionFactory;
37 import org.hibernate.TransientObjectException;
38 import org.hibernate.criterion.CriteriaSpecification;
39 import org.hibernate.criterion.DetachedCriteria;
40 import org.hibernate.criterion.Order;
41 import org.hibernate.criterion.ProjectionList;
42 import org.hibernate.criterion.Projections;
43 import org.hibernate.criterion.Restrictions;
44 import org.hibernate.metadata.ClassMetadata;
45 import org.hibernate.metadata.CollectionMetadata;
46 import org.hibernate.proxy.HibernateProxy;
47 import org.hibernate.type.CollectionType;
48 import org.hibernate.type.EntityType;
49 import org.hibernate.type.Type;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.beans.factory.InitializingBean;
53 import org.springframework.dao.DataAccessException;
54 import org.springframework.dao.DataIntegrityViolationException;
55 import org.springframework.dao.DataRetrievalFailureException;
56 import org.springframework.dao.OptimisticLockingFailureException;
57 import org.springframework.transaction.annotation.Propagation;
58 import org.springframework.transaction.annotation.Transactional;
59
60 import ch.elca.el4j.services.persistence.generic.dao.annotations.ReturnsUnchangedParameter;
61 import ch.elca.el4j.services.persistence.hibernate.criteria.CriteriaTransformer;
62 import ch.elca.el4j.services.persistence.hibernate.dao.extent.DataExtent;
63 import ch.elca.el4j.services.persistence.hibernate.dao.extent.ExtentCollection;
64 import ch.elca.el4j.services.persistence.hibernate.dao.extent.ExtentEntity;
65 import ch.elca.el4j.services.search.QueryObject;
66 import ch.elca.el4j.util.codingsupport.Reject;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 public class GenericHibernateDao<T, ID extends Serializable>
84 extends ConvenienceHibernateDaoSupport
85 implements ConvenienceGenericHibernateDao<T, ID>, InitializingBean {
86
87
88
89
90
91 private static final int MAX_BULK_DELETE = 100;
92
93
94
95
96 private static Logger s_logger = LoggerFactory.getLogger(GenericHibernateDao.class);
97
98
99
100
101 private Class<T> m_persistentClass;
102
103
104
105
106 private Order[] m_defaultOrder = null;
107
108
109
110
111 @SuppressWarnings("unchecked")
112 public GenericHibernateDao() {
113 try {
114 this.m_persistentClass = (Class<T>) ((ParameterizedType) getClass()
115 .getGenericSuperclass()).getActualTypeArguments()[0];
116 } catch (Exception e) {
117
118
119 }
120 }
121
122
123
124
125
126
127
128
129 public void setPersistentClass(Class<T> c) {
130 Reject.ifNull(c);
131 m_persistentClass = c;
132 }
133
134
135
136
137 public Class<T> getPersistentClass() {
138 assert m_persistentClass != null;
139 return m_persistentClass;
140 }
141
142
143 public Order[] getDefaultOrder() {
144 return m_defaultOrder;
145 }
146
147
148 public void setDefaultOrder(Order... defaultOrder) {
149 m_defaultOrder = defaultOrder;
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 @SuppressWarnings("unchecked")
173 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
174 public T findById(ID id, boolean lock)
175 throws DataAccessException, DataRetrievalFailureException {
176
177 T entity;
178 if (lock) {
179 entity = (T) getConvenienceHibernateTemplate().get(getPersistentClass(), id, LockMode.UPGRADE);
180 } else {
181 entity = (T) getConvenienceHibernateTemplate().get(getPersistentClass(), id);
182 }
183 if (entity == null) {
184 throw new DataRetrievalFailureException("The desired domain object could not be retrieved.");
185 }
186 return entity;
187 }
188
189
190
191
192 @SuppressWarnings("unchecked")
193 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
194 public T findById(ID id, boolean lock, DataExtent extent)
195 throws DataAccessException, DataRetrievalFailureException {
196
197 T entity;
198 if (lock) {
199 entity = (T) getConvenienceHibernateTemplate().get(getPersistentClass(), id, LockMode.UPGRADE);
200 } else {
201 entity = (T) getConvenienceHibernateTemplate().get(getPersistentClass(), id);
202 }
203 if (entity == null) {
204 throw new DataRetrievalFailureException("The desired domain object could not be retrieved.");
205 }
206 return fetchExtent(entity, extent);
207 }
208
209
210
211
212 @SuppressWarnings("unchecked")
213 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
214 public T findById(ID id)
215 throws DataAccessException, DataRetrievalFailureException {
216 return (T) getConvenienceHibernateTemplate().getByIdStrong(
217 getPersistentClass(), id, getPersistentClassName());
218 }
219
220
221
222
223 @SuppressWarnings("unchecked")
224 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
225 public T findById(ID id, DataExtent extent)
226 throws DataAccessException, DataRetrievalFailureException {
227 return fetchExtent((T) getConvenienceHibernateTemplate().getByIdStrong(
228 getPersistentClass(), id, getPersistentClassName()), extent);
229 }
230
231
232
233
234 @SuppressWarnings("unchecked")
235 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
236 public T findByIdLazy(ID id)
237 throws DataAccessException, DataRetrievalFailureException {
238 return (T) getConvenienceHibernateTemplate().getByIdStrongLazy(
239 getPersistentClass(), id, getPersistentClassName());
240 }
241
242
243
244
245 @SuppressWarnings("unchecked")
246 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
247 public List<T> getAll() throws DataAccessException {
248 return getConvenienceHibernateTemplate().findByCriteria(getOrderedCriteria());
249 }
250
251
252
253
254 @SuppressWarnings("unchecked")
255 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
256 public List<T> getAll(DataExtent extent) throws DataAccessException {
257 return fetchExtent(getConvenienceHibernateTemplate().findByCriteria(getOrderedCriteria()), extent);
258 }
259
260
261
262
263
264
265
266
267
268 @SuppressWarnings("unchecked")
269 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
270 public List<T> findByQuery(QueryObject q) throws DataAccessException {
271 DetachedCriteria hibernateCriteria = getCriteria(q);
272
273 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
274
275 return template.findByCriteria(hibernateCriteria, q.getFirstResult(), q.getMaxResults());
276 }
277
278
279
280
281
282
283
284
285 @SuppressWarnings("unchecked")
286 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
287 public List<T> findByQuery(QueryObject q, DataExtent extent) throws DataAccessException {
288 DetachedCriteria hibernateCriteria = getCriteria(q);
289
290 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
291
292 return fetchExtent(template.findByCriteria(hibernateCriteria, q.getFirstResult(),
293 q.getMaxResults()), extent);
294 }
295
296
297
298
299
300
301
302
303
304 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
305 public int findCountByQuery(QueryObject q) throws DataAccessException {
306 DetachedCriteria hibernateCriteria = getCriteria(q);
307
308 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
309
310 return template.findCountByCriteria(hibernateCriteria);
311 }
312
313
314 @SuppressWarnings("unchecked")
315 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
316 public List<T> findByCriteria(DetachedCriteria hibernateCriteria)
317 throws DataAccessException {
318
319 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
320
321 return template.findByCriteria(hibernateCriteria);
322 }
323
324
325 @SuppressWarnings("unchecked")
326 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
327 public List<T> findByCriteria(DetachedCriteria hibernateCriteria, DataExtent extent)
328 throws DataAccessException {
329
330 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
331
332 return fetchExtent(template.findByCriteria(hibernateCriteria), extent);
333 }
334
335
336 @SuppressWarnings("unchecked")
337 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
338 public List<T> findByCriteria(DetachedCriteria hibernateCriteria, int firstResult, int maxResults)
339 throws DataAccessException {
340
341 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
342
343 return template.findByCriteria(hibernateCriteria, firstResult, maxResults);
344 }
345
346
347 @SuppressWarnings("unchecked")
348 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
349 public List<T> findByCriteria(DetachedCriteria hibernateCriteria, int firstResult, int maxResults,
350 DataExtent extent) throws DataAccessException {
351
352 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
353
354 return fetchExtent(template.findByCriteria(hibernateCriteria, firstResult, maxResults), extent);
355 }
356
357
358 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
359 public int findCountByCriteria(DetachedCriteria hibernateCriteria)
360 throws DataAccessException {
361
362 ConvenienceHibernateTemplate template = getConvenienceHibernateTemplate();
363
364 return template.findCountByCriteria(hibernateCriteria);
365 }
366
367
368 @ReturnsUnchangedParameter
369 @Transactional(propagation = Propagation.REQUIRED)
370 public T saveOrUpdate(T entity) throws DataAccessException,
371 DataIntegrityViolationException, OptimisticLockingFailureException {
372
373 getConvenienceHibernateTemplate().saveOrUpdateStrong(entity, getPersistentClassName());
374 return entity;
375 }
376
377
378
379 @ReturnsUnchangedParameter
380 @Transactional(propagation = Propagation.REQUIRED)
381 public T saveOrUpdateAndFlush(T entity) throws DataAccessException,
382 DataIntegrityViolationException, OptimisticLockingFailureException {
383
384 T tmp = saveOrUpdate(entity);
385 flush();
386 return tmp;
387 }
388
389
390 @Transactional(propagation = Propagation.REQUIRED)
391 public void delete(T entity) throws DataAccessException {
392 getConvenienceHibernateTemplate().delete(entity);
393 }
394
395
396 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
397 public T refresh(T entity) throws DataAccessException,
398 DataRetrievalFailureException {
399 getConvenienceHibernateTemplate().refresh(entity);
400 return entity;
401 }
402
403
404 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
405 public T refresh(T entity, DataExtent extent) throws DataAccessException,
406 DataRetrievalFailureException {
407 getConvenienceHibernateTemplate().refresh(entity);
408 return fetchExtent(entity, extent);
409 }
410
411
412 @SuppressWarnings("unchecked")
413 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
414 public T reload(T entity) throws DataAccessException,
415 DataRetrievalFailureException {
416 ID id = (ID) getSessionFactory().getClassMetadata(entity.getClass())
417 .getIdentifier(entity, EntityMode.POJO);
418 return findById(id);
419 }
420
421
422 @SuppressWarnings("unchecked")
423 @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
424 public T reload(T entity, DataExtent extent) throws DataAccessException,
425 DataRetrievalFailureException {
426 ID id = (ID) getSessionFactory().getClassMetadata(entity.getClass())
427 .getIdentifier(entity, EntityMode.POJO);
428 T refresh = (T) findById(id, extent);
429 return refresh;
430
431 }
432
433
434 @Deprecated
435 @Transactional(propagation = Propagation.REQUIRED)
436 public void delete(ID id) throws DataAccessException {
437 getConvenienceHibernateTemplate().deleteStrong(getPersistentClass(),
438 id, getPersistentClassName());
439 }
440
441
442 @Transactional(propagation = Propagation.REQUIRED)
443 public void deleteById(ID id) throws DataAccessException {
444 getConvenienceHibernateTemplate().deleteStrong(getPersistentClass(),
445 id, getPersistentClassName());
446 }
447
448
449 public void delete(Collection<T> entities) throws DataAccessException,
450 DataIntegrityViolationException, OptimisticLockingFailureException {
451 getConvenienceHibernateTemplate().deleteAll(entities);
452 }
453
454
455 public void deleteNoCascade(Collection<T> entities) throws DataAccessException,
456 DataIntegrityViolationException, OptimisticLockingFailureException {
457
458
459 Method[] methods = getPersistentClass().getMethods();
460 Method idMethod = null;
461 for (Method m : methods) {
462 if (m.isAnnotationPresent(javax.persistence.Id.class)) {
463 idMethod = m;
464 }
465 }
466
467 if (idMethod != null) {
468
469 ArrayList<Object> hqlParameter = new ArrayList<Object>();
470 StringBuilder hqlQuery = new StringBuilder("delete ");
471 hqlQuery.append(getPersistentClassName());
472 hqlQuery.append(" where ");
473
474 Iterator<T> it = entities.iterator();
475 T entity;
476 boolean fallback;
477 int querycount = 0;
478
479
480 while (it.hasNext()) {
481 fallback = false;
482 entity = it.next();
483 Object o = null;
484 try {
485 o = idMethod.invoke(entity);
486 } catch (IllegalArgumentException e) {
487 fallback = true;
488 } catch (IllegalAccessException e) {
489 fallback = true;
490 } catch (InvocationTargetException e) {
491 fallback = true;
492 }
493 if (o == null) {
494 fallback = true;
495 }
496
497 if (!fallback) {
498
499
500 if (querycount >= MAX_BULK_DELETE) {
501
502 getHibernateTemplate().bulkUpdate(hqlQuery.toString(), hqlParameter.toArray());
503
504 hqlParameter.clear();
505 hqlQuery = new StringBuilder("delete ");
506 hqlQuery.append(getPersistentClassName());
507 hqlQuery.append(" where ");
508 querycount = 0;
509 }
510 if (querycount > 0) {
511 hqlQuery.append("or ");
512 }
513 hqlQuery.append("id = ? ");
514 hqlParameter.add(o);
515 querycount++;
516 } else {
517
518
519 s_logger.warn(idMethod.getName() + "could not be called in " + getPersistentClassName()
520 + ". Not using HQL bulk delete for this entity.");
521 getConvenienceHibernateTemplate().delete(entity);
522 }
523 }
524
525
526 if (querycount > 0) {
527 getHibernateTemplate().bulkUpdate(hqlQuery.toString(), hqlParameter.toArray());
528 }
529
530 } else {
531
532
533 s_logger.warn("No @Id annotation was found in " + getPersistentClassName()
534 + ". Not using HQL bulk delete for all entities.");
535 getConvenienceHibernateTemplate().deleteAll(entities);
536 }
537 }
538
539
540
541 public void deleteAll()
542 throws OptimisticLockingFailureException, DataAccessException {
543 List<T> list = getAll();
544 if (list.size() > 0) {
545 delete(list);
546 }
547 }
548
549
550
551 public void deleteAllNoCascade()
552 throws OptimisticLockingFailureException, DataAccessException {
553
554 String hqlQuery = "delete " + getPersistentClassName();
555 getHibernateTemplate().bulkUpdate(hqlQuery);
556 }
557
558
559 @Transactional(propagation = Propagation.REQUIRED)
560 public void flush() {
561 getConvenienceHibernateTemplate().flush();
562 }
563
564
565 public DetachedCriteria getOrderedCriteria() {
566 DetachedCriteria criteria = DetachedCriteria.forClass(getPersistentClass());
567
568 return addOrder(makeDistinct(criteria));
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582 protected List<T> fetchExtent(List<T> objects, DataExtent extent)
583 throws DataAccessException {
584
585 if (extent != null) {
586 ReferenceMap fetchedObjects = new ReferenceMap();
587 for (Object obj : objects) {
588 fetchExtentObject(obj, extent.getRootEntity(), fetchedObjects);
589 }
590 }
591 return objects;
592 }
593
594
595
596
597
598
599
600
601
602
603
604
605 protected T fetchExtent(T object, DataExtent extent)
606 throws DataAccessException {
607
608 if (extent != null) {
609 ReferenceMap fetchedObjects = new ReferenceMap();
610 fetchExtentObject(object, extent.getRootEntity(), fetchedObjects);
611 }
612 return object;
613 }
614
615
616
617
618
619
620
621
622
623
624 private void fetchExtentObject(Object object, ExtentEntity entity, ReferenceMap fetchedObjects)
625 throws DataAccessException {
626
627 Object[] nullArg = null;
628 if (object == null || entity == null || fetchedObjects == null) {
629 return;
630 }
631 fetchedObjects.put(object, entity);
632 try {
633
634 if (entity.isRoot()) {
635 fetchExtentUsingJoinQuery(object, entity);
636 }
637
638
639
640 for (ExtentEntity ent : entity.getChildEntities()) {
641 Object obj = ent.getMethod().invoke(object, nullArg);
642
643 if (obj instanceof HibernateProxy && !Hibernate.isInitialized(obj)) {
644 Hibernate.initialize(obj);
645 }
646 if (!fetchedObjects.containsKey(obj) || !fetchedObjects.get(obj).equals(ent)) {
647 fetchExtentObject(obj, ent, fetchedObjects);
648 }
649 }
650
651
652 for (ExtentCollection c : entity.getCollections()) {
653 Collection<?> coll = (Collection<?>) c.getMethod().invoke(object, nullArg);
654 if (coll != null) {
655 for (Object o : coll) {
656
657 if (o instanceof HibernateProxy && !Hibernate.isInitialized(o)) {
658 Hibernate.initialize(o);
659 }
660 if (!fetchedObjects.containsKey(o) || !fetchedObjects.get(o).equals(c.getContainedEntity())) {
661 fetchExtentObject(o, c.getContainedEntity(), fetchedObjects);
662 }
663 }
664 }
665 }
666 } catch (Exception e) {
667 throw new RuntimeException(e);
668 }
669
670 }
671
672
673
674
675
676
677
678
679
680
681
682 private void fetchExtentUsingJoinQuery(Object object, ExtentEntity entity) {
683 ID id;
684 ClassMetadata metadata = getSessionFactory().getClassMetadata(object.getClass());
685 if (metadata == null) {
686
687 return;
688 }
689 id = (ID) metadata.getIdentifier(object, EntityMode.POJO);
690
691 Criteria criteria = getSession().createCriteria(entity.getEntityClass());
692 criteria.add(Restrictions.idEq(id));
693 if (buildJoinCriteria(criteria, entity)) {
694
695 criteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
696 criteria.list();
697 }
698 }
699
700
701
702
703
704
705
706
707
708
709 private boolean buildJoinCriteria(Criteria criteria, ExtentEntity rootEntity) {
710
711 return buildJoinCriteria(criteria, rootEntity, null);
712 }
713
714
715
716
717
718
719
720
721
722
723 private boolean buildJoinCriteria(Criteria criteria, ExtentEntity entity, String alias) {
724 ClassMetadata metadata = getSessionFactory().getClassMetadata(entity.getEntityClass());
725
726 boolean couldJoin = false;
727
728 if (metadata == null) {
729
730 return false;
731 }
732
733 String prefix;
734 String aliasPrefix;
735 if (alias == null) {
736 prefix = "";
737 aliasPrefix = "ALIAS_";
738 } else {
739 aliasPrefix = alias + "_";
740 prefix = alias + ".";
741 }
742
743
744 for (String field : entity.getFields()) {
745 try {
746 metadata.getPropertyType(field);
747 } catch (HibernateException e) {
748
749 continue;
750 }
751 criteria.setFetchMode(prefix + field, FetchMode.JOIN);
752 }
753
754
755 for (ExtentEntity e : entity.getChildEntities()) {
756 Type type;
757 try {
758 type = metadata.getPropertyType(e.getName());
759 } catch (HibernateException he) {
760
761
762
763 continue;
764 }
765 if (type instanceof EntityType) {
766 String fieldAlias = aliasPrefix + e.getName();
767 criteria.createAlias(prefix + e.getName(), fieldAlias, Criteria.LEFT_JOIN);
768 couldJoin = true;
769 criteria.setFetchMode(prefix + e.getName(), FetchMode.JOIN);
770 buildJoinCriteria(criteria, e, fieldAlias);
771 } else {
772 criteria.setFetchMode(prefix + e.getName(), FetchMode.JOIN);
773 }
774 }
775
776
777
778
779 return couldJoin;
780 }
781
782
783
784
785
786
787
788
789 protected String getPersistentClassName() {
790 return getPersistentClass().getSimpleName();
791 }
792
793
794
795
796
797 protected DetachedCriteria getCriteria(QueryObject queryObject) {
798 DetachedCriteria criteria = CriteriaTransformer.transform(queryObject, getPersistentClass());
799
800 if (queryObject.getOrderConstraints().size() == 0) {
801 criteria = addOrder(criteria);
802 }
803
804 return makeDistinct(criteria);
805 }
806
807
808
809
810
811
812 protected DetachedCriteria addOrder(DetachedCriteria criteria) {
813 if (m_defaultOrder != null) {
814 for (Order order : m_defaultOrder) {
815 criteria.addOrder(order);
816 }
817 }
818 return criteria;
819 }
820
821
822
823
824
825 protected DetachedCriteria makeDistinct(DetachedCriteria criteria) {
826 criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
827
828 return criteria;
829 }
830
831 }