Merge remote-tracking branch 'origin/cdm-4.7' into feature/cdm-4.7
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / common / CdmEntityDaoBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.persistence.dao.hibernate.common;
11
12 import java.lang.reflect.Field;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.UUID;
22
23 import org.apache.log4j.Logger;
24 import org.apache.lucene.search.Sort;
25 import org.apache.lucene.search.SortField;
26 import org.hibernate.Criteria;
27 import org.hibernate.FlushMode;
28 import org.hibernate.HibernateException;
29 import org.hibernate.LockOptions;
30 import org.hibernate.NonUniqueObjectException;
31 import org.hibernate.Query;
32 import org.hibernate.Session;
33 import org.hibernate.criterion.Criterion;
34 import org.hibernate.criterion.DetachedCriteria;
35 import org.hibernate.criterion.Example;
36 import org.hibernate.criterion.Example.PropertySelector;
37 import org.hibernate.criterion.Order;
38 import org.hibernate.criterion.ProjectionList;
39 import org.hibernate.criterion.Projections;
40 import org.hibernate.criterion.Restrictions;
41 import org.hibernate.envers.query.AuditQuery;
42 import org.hibernate.metadata.ClassMetadata;
43 import org.hibernate.search.FullTextQuery;
44 import org.hibernate.type.Type;
45 import org.joda.time.DateTime;
46 import org.springframework.beans.factory.annotation.Autowired;
47 import org.springframework.dao.DataAccessException;
48 import org.springframework.dao.InvalidDataAccessApiUsageException;
49 import org.springframework.security.core.Authentication;
50 import org.springframework.security.core.context.SecurityContextHolder;
51 import org.springframework.stereotype.Repository;
52 import org.springframework.util.ReflectionUtils;
53
54 import eu.etaxonomy.cdm.model.common.CdmBase;
55 import eu.etaxonomy.cdm.model.common.User;
56 import eu.etaxonomy.cdm.model.common.VersionableEntity;
57 import eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao;
58 import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
59 import eu.etaxonomy.cdm.persistence.dto.MergeResult;
60 import eu.etaxonomy.cdm.persistence.hibernate.PostMergeEntityListener;
61 import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadata;
62 import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadataFactory;
63 import eu.etaxonomy.cdm.persistence.query.Grouping;
64 import eu.etaxonomy.cdm.persistence.query.MatchMode;
65 import eu.etaxonomy.cdm.persistence.query.OrderHint;
66
67
68 /**
69 * @author a.mueller
70 * FIXME CdmEntityDaoBase is abstract, can it be annotated with @Repository?
71 */
72 @Repository
73 public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implements ICdmEntityDao<T> {
74
75 private static final Logger logger = Logger.getLogger(CdmEntityDaoBase.class);
76
77 protected int flushAfterNo = 1000; //large numbers may cause synchronisation errors when commiting the session !!
78
79 protected Class<T> type;
80
81 // protected Version version = Configuration.luceneVersion;
82
83 @Autowired
84 // @Qualifier("defaultBeanInitializer")
85 protected IBeanInitializer defaultBeanInitializer;
86
87 public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer) {
88 this.defaultBeanInitializer = defaultBeanInitializer;
89 }
90
91 @Autowired
92 private ReferringObjectMetadataFactory referringObjectMetadataFactory;
93
94
95 public CdmEntityDaoBase(Class<T> type){
96 this.type = type;
97 logger.debug("Creating DAO of type [" + type.getSimpleName() + "]");
98 }
99
100 @Override
101 public void lock(T t, LockOptions lockOptions) {
102 getSession().buildLockRequest(lockOptions).lock(t);
103 }
104
105 @Override
106 public void refresh(T t, LockOptions lockOptions, List<String> propertyPaths) {
107 getSession().refresh(t, lockOptions);
108 defaultBeanInitializer.initialize(t, propertyPaths);
109 }
110
111 //TODO this method should be moved to a concrete class (not typed)
112 public UUID saveCdmObj(CdmBase cdmObj) throws DataAccessException {
113 getSession().saveOrUpdate(cdmObj);
114 return cdmObj.getUuid();
115 }
116
117 //TODO: Replace saveCdmObj() by saveCdmObject_
118 private UUID saveCdmObject_(T cdmObj){
119 getSession().saveOrUpdate(cdmObj);
120 return cdmObj.getUuid();
121 }
122
123 //TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of ServiceBase.saveCdmObjectAll()?
124 //TODO: why does this use saveCdmObject_ which actually savesOrUpdateds data ?
125 @Override
126 public Map<UUID, T> saveAll(Collection<T> cdmObjCollection){
127 int types = cdmObjCollection.getClass().getTypeParameters().length;
128 if (types > 0){
129 if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}
130 }
131
132 Map<UUID, T> resultMap = new HashMap<UUID, T>();
133 Iterator<T> iterator = cdmObjCollection.iterator();
134 int i = 0;
135 while(iterator.hasNext()){
136 if ( ( (i % 2000) == 0) && (i > 0) ){logger.debug("Saved " + i + " objects" );}
137 T cdmObj = iterator.next();
138 UUID uuid = saveCdmObject_(cdmObj);
139 if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}
140 resultMap.put(uuid, cdmObj);
141 i++;
142 if ( (i % flushAfterNo) == 0){
143 try{
144 if (logger.isDebugEnabled()){logger.debug("flush");}
145 flush();
146 }catch(Exception e){
147 logger.error("An exception occurred when trying to flush data");
148 e.printStackTrace();
149 throw new RuntimeException(e);
150 }
151 }
152 }
153
154 if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}
155 return resultMap;
156 }
157
158 private UUID saveOrUpdateCdmObject(T cdmObj){
159 getSession().saveOrUpdate(cdmObj);
160 return cdmObj.getUuid();
161 }
162
163 @Override
164 public Map<UUID, T> saveOrUpdateAll(Collection<T> cdmObjCollection){
165 int types = cdmObjCollection.getClass().getTypeParameters().length;
166 if (types > 0){
167 if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}
168 }
169
170 Map<UUID, T> resultMap = new HashMap<>();
171 Iterator<T> iterator = cdmObjCollection.iterator();
172 int i = 0;
173 while(iterator.hasNext()){
174 if ( ( (i % 2000) == 0) && (i > 0) ){logger.debug("Saved " + i + " objects" );}
175 T cdmObj = iterator.next();
176 UUID uuid = saveOrUpdateCdmObject(cdmObj);
177 if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}
178 resultMap.put(uuid, cdmObj);
179 i++;
180 if ( (i % flushAfterNo) == 0){
181 try{
182 if (logger.isDebugEnabled()){logger.debug("flush");}
183 flush();
184 }catch(Exception e){
185 logger.error("An exception occurred when trying to flush data");
186 e.printStackTrace();
187 throw new RuntimeException(e);
188 }
189 }
190 }
191
192 if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}
193 return resultMap;
194 }
195
196
197
198
199 @Override
200 public T replace(T x, T y) {
201 if(x.equals(y)) {
202 return y;
203 }
204
205 Class<?> commonClass = x.getClass();
206 if(y != null) {
207 while(!commonClass.isAssignableFrom(y.getClass())) {
208 if(commonClass.equals(type)) {
209 throw new RuntimeException();
210 }
211 commonClass = commonClass.getSuperclass();
212 }
213 }
214
215 getSession().merge(x);
216
217 Set<ReferringObjectMetadata> referringObjectMetas = referringObjectMetadataFactory.get(x.getClass());
218
219 for(ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {
220
221 List<CdmBase> referringObjects = referringObjectMetadata.getReferringObjects(x, getSession());
222
223 for(CdmBase referringObject : referringObjects) {
224 try {
225 referringObjectMetadata.replace(referringObject,x,y);
226 getSession().update(referringObject);
227
228 } catch (IllegalArgumentException e) {
229 throw new RuntimeException(e.getMessage(),e);
230 } catch (IllegalAccessException e) {
231 throw new RuntimeException(e.getMessage(),e);
232 }
233 }
234 }
235 return y;
236 }
237
238 @Override
239 public Session getSession() throws DataAccessException {
240 return super.getSession();
241 }
242
243 @Override
244 public void clear() throws DataAccessException {
245 Session session = getSession();
246 session.clear();
247 if (logger.isDebugEnabled()){logger.debug("dao clear end");}
248 }
249
250 @Override
251 public MergeResult<T> merge(T transientObject, boolean returnTransientEntity) throws DataAccessException {
252 Session session = getSession();
253 PostMergeEntityListener.addSession(session);
254 MergeResult<T> result = null;
255 try {
256 @SuppressWarnings("unchecked")
257 T persistentObject = (T)session.merge(transientObject);
258 if (logger.isDebugEnabled()){logger.debug("dao merge end");}
259
260 if(returnTransientEntity) {
261 if(transientObject != null && persistentObject != null) {
262 transientObject.setId(persistentObject.getId());
263 }
264 result = new MergeResult(transientObject, PostMergeEntityListener.getNewEntities(session));
265 } else {
266 result = new MergeResult(persistentObject, null);
267 }
268 return result;
269 } finally {
270 PostMergeEntityListener.removeSession(session);
271 }
272 }
273
274 @Override
275 public T merge(T transientObject) throws DataAccessException {
276 Session session = getSession();
277 @SuppressWarnings("unchecked")
278 T persistentObject = (T)session.merge(transientObject);
279 if (logger.isDebugEnabled()){logger.debug("dao merge end");}
280 return persistentObject;
281 }
282
283 @Override
284 public UUID saveOrUpdate(T transientObject) throws DataAccessException {
285 if (transientObject == null){
286 logger.warn("Object to save should not be null. NOP");
287 return null;
288 }
289 try {
290 if (logger.isDebugEnabled()){logger.debug("dao saveOrUpdate start...");}
291 if (logger.isDebugEnabled()){logger.debug("transientObject(" + transientObject.getClass().getSimpleName() + ") ID:" + transientObject.getId() + ", UUID: " + transientObject.getUuid()) ;}
292 Session session = getSession();
293 if(transientObject.getId() != 0 && VersionableEntity.class.isAssignableFrom(transientObject.getClass())) {
294 VersionableEntity versionableEntity = (VersionableEntity)transientObject;
295 versionableEntity.setUpdated(new DateTime());
296 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
297 if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
298 User user = (User)authentication.getPrincipal();
299 versionableEntity.setUpdatedBy(user);
300 }
301 }
302 session.saveOrUpdate(transientObject);
303 if (logger.isDebugEnabled()){logger.debug("dao saveOrUpdate end");}
304 return transientObject.getUuid();
305 } catch (NonUniqueObjectException e) {
306 logger.error("Error in CdmEntityDaoBase.saveOrUpdate(obj)");
307 logger.error(e.getIdentifier());
308 logger.error(e.getEntityName());
309 logger.error(e.getMessage());
310
311 e.printStackTrace();
312 throw e;
313 } catch (HibernateException e) {
314
315 e.printStackTrace();
316 throw e;
317 }
318 }
319
320 @Override
321 public T save(T newInstance) throws DataAccessException {
322 if (newInstance == null){
323 logger.warn("Object to save should not be null. NOP");
324 return null;
325 }
326 getSession().save(newInstance);
327 return newInstance;
328 }
329
330 @Override
331 public UUID update(T transientObject) throws DataAccessException {
332 if (transientObject == null){
333 logger.warn("Object to update should not be null. NOP");
334 return null;
335 }
336 getSession().update(transientObject);
337 return transientObject.getUuid();
338 }
339
340 @Override
341 public UUID refresh(T persistentObject) throws DataAccessException {
342 getSession().refresh(persistentObject);
343 return persistentObject.getUuid();
344 }
345
346 @Override
347 public UUID delete(T persistentObject) throws DataAccessException {
348 if (persistentObject == null){
349 logger.warn(type.getName() + " was 'null'");
350 return null;
351 }
352
353 // Merge the object in if it is detached
354 //
355 // I think this is preferable to catching lazy initialization errors
356 // as that solution only swallows and hides the exception, but doesn't
357 // actually solve it.
358 persistentObject = (T) getSession().merge(persistentObject);
359 getSession().delete(persistentObject);
360 return persistentObject.getUuid();
361 }
362
363 @Override
364 public T findById(int id) throws DataAccessException {
365 return getSession().get(type, id);
366 }
367
368
369 @Override
370 public T findByUuid(UUID uuid) throws DataAccessException{
371 Session session = getSession();
372 Criteria crit = session.createCriteria(type);
373 crit.add(Restrictions.eq("uuid", uuid));
374 crit.addOrder(Order.desc("created"));
375 @SuppressWarnings("unchecked")
376 List<T> results = crit.list();
377 Set<T> resultSet = new HashSet<>();
378 resultSet.addAll(results);
379 if (resultSet.isEmpty()){
380 return null;
381 }else{
382 if(resultSet.size() > 1){
383 logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
384 }
385 return results.get(0);
386 }
387 }
388
389 @Override
390 public T findByUuidWithoutFlush(UUID uuid) throws DataAccessException{
391 Session session = getSession();
392 FlushMode currentFlushMode = session.getFlushMode();
393 try {
394 // set flush mode to manual so that the session does not flush
395 // when before performing the query
396 session.setFlushMode(FlushMode.MANUAL);
397 Criteria crit = session.createCriteria(type);
398 crit.add(Restrictions.eq("uuid", uuid));
399 crit.addOrder(Order.desc("created"));
400 @SuppressWarnings("unchecked")
401 List<T> results = crit.list();
402 if (results.isEmpty()){
403 return null;
404 }else{
405 if(results.size() > 1){
406 logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
407 }
408 return results.get(0);
409 }
410 } finally {
411 // set back the session flush mode
412 if(currentFlushMode != null) {
413 session.setFlushMode(currentFlushMode);
414 }
415 }
416 }
417
418 @Override
419 public List<T> loadList(Collection<Integer> ids, List<String> propertyPaths) throws DataAccessException {
420
421 if (ids.isEmpty()) {
422 return new ArrayList<T>(0);
423 }
424
425 Criteria criteria = prepareList(ids, null, null, null, "id");
426
427 if (logger.isDebugEnabled()){logger.debug(criteria.toString());}
428
429 @SuppressWarnings("unchecked")
430 List<T> result = criteria.list();
431 defaultBeanInitializer.initializeAll(result, propertyPaths);
432 return result;
433 }
434
435
436 @Override
437 public List<T> list(Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {
438
439 if (uuids == null || uuids.isEmpty()){
440 return new ArrayList<>();
441 }
442
443 Criteria criteria = prepareList(uuids, pageSize, pageNumber, orderHints, "uuid");
444
445 @SuppressWarnings("unchecked")
446 List<T> result = criteria.list();
447 defaultBeanInitializer.initializeAll(result, propertyPaths);
448 return result;
449 }
450
451 /**
452 * {@inheritDoc}
453 */
454 @Override
455 public List<T> list(Class<? extends T> type, String propertyName, Object value, MatchMode matchMode,
456 Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
457
458 Criteria criteria = criterionForType(type);
459
460 if (propertyName != null) {
461 addRestriction(propertyName, value, matchMode, criteria);
462 }
463
464 addLimitAndStart(limit, start, criteria);
465 addOrder(criteria, orderHints);
466
467 @SuppressWarnings("unchecked")
468 List<T> result = criteria.list();
469 defaultBeanInitializer.initializeAll(result, propertyPaths);
470 return result;
471 }
472
473 /**
474 * @param propertyName
475 * @param value
476 * @param matchMode
477 * @param criteria
478 */
479 private void addRestriction(String propertyName, Object value, MatchMode matchMode, Criteria criteria) {
480 Criterion restriction;
481 if(matchMode == null) {
482 restriction = Restrictions.eq(propertyName, value);
483 } else if(value == null) {
484 restriction = Restrictions.isNull(propertyName);
485 } else if(!(value instanceof String)) {
486 restriction = Restrictions.eq(propertyName, value);
487 } else {
488 String queryString = (String)value;
489 if(matchMode == MatchMode.BEGINNING) {
490 restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.START);
491 } else if(matchMode == MatchMode.END) {
492 restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.END);
493 } else if(matchMode == MatchMode.EXACT) {
494 restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.EXACT);
495 } else {
496 restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.ANYWHERE);
497 }
498 }
499 criteria.add(restriction);
500 }
501
502 /**
503 * {@inheritDoc}
504 */
505 @Override
506 public int count(Class<? extends T> type, String propertyName, Object value, MatchMode matchMode) {
507
508 Criteria criteria = criterionForType(type);
509
510 if (propertyName != null) {
511 addRestriction(propertyName, value, matchMode, criteria);
512 }
513
514 criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
515
516 //since hibernate 4 (or so) uniqueResult returns Long, not Integer, therefore needs
517 //to be casted. Think about returning long rather then int!
518 return ((Number) criteria.uniqueResult()).intValue();
519
520 }
521
522 /**
523 * @param uuids
524 * @param pageSize
525 * @param pageNumber
526 * @param orderHints
527 * @param propertyName
528 * @return
529 */
530 private Criteria prepareList(Collection<?> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, String propertyName) {
531 Criteria criteria = getSession().createCriteria(type);
532 criteria.add(Restrictions.in(propertyName, uuids));
533
534 if(pageSize != null) {
535 criteria.setMaxResults(pageSize);
536 if(pageNumber != null) {
537 criteria.setFirstResult(pageNumber * pageSize);
538 } else {
539 criteria.setFirstResult(0);
540 }
541 }
542
543 if(orderHints == null) {
544 orderHints = OrderHint.defaultOrderHintsFor(type);
545 }
546 addOrder(criteria, orderHints);
547 return criteria;
548 }
549
550 /**
551 *
552 * NOTE: We can't reuse {@link #list(Class, String, Object, MatchMode, Integer, Integer, List, List)
553 * here due to different default behavior of the <code>matchmode</code> parameter.
554 *
555 * @param clazz
556 * @param param
557 * @param queryString
558 * @param matchmode
559 * @param criterion
560 * @param pageSize
561 * @param pageNumber
562 * @param orderHints
563 * @param propertyPaths
564 * @return
565 */
566 protected List<T> findByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode, List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
567
568 Criteria criteria = criterionForType(clazz);
569
570 if (queryString != null) {
571 if(matchmode == null) {
572 criteria.add(Restrictions.ilike(param, queryString));
573 } else if(matchmode == MatchMode.BEGINNING) {
574 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));
575 } else if(matchmode == MatchMode.END) {
576 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));
577 } else if(matchmode == MatchMode.EXACT) {
578 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));
579 } else {
580 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));
581 }
582 }
583
584 addCriteria(criteria, criterion);
585
586 if(pageSize != null) {
587 criteria.setMaxResults(pageSize);
588 if(pageNumber != null) {
589 criteria.setFirstResult(pageNumber * pageSize);
590 } else {
591 criteria.setFirstResult(0);
592 }
593 }
594
595 addOrder(criteria, orderHints);
596
597 @SuppressWarnings("unchecked")
598 List<T> result = criteria.list();
599 defaultBeanInitializer.initializeAll(result, propertyPaths);
600 return result;
601 }
602
603 /**
604 * @param clazz
605 * @return
606 */
607 private Criteria criterionForType(Class<? extends T> clazz) {
608 Criteria criteria;
609 if(clazz == null) {
610 criteria = getSession().createCriteria(type);
611 } else {
612 criteria = getSession().createCriteria(clazz);
613 }
614 return criteria;
615 }
616
617 @Override
618 public T load(UUID uuid) {
619 T bean = findByUuid(uuid);
620 if(bean == null){
621 return null;
622 }
623 defaultBeanInitializer.load(bean);
624
625 return bean;
626 }
627
628 @Override
629 public T load(int id, List<String> propertyPaths){
630 T bean = findById(id);
631 if(bean == null){
632 return bean;
633 }
634 defaultBeanInitializer.initialize(bean, propertyPaths);
635
636 return bean;
637 }
638
639 @Override
640 public T load(UUID uuid, List<String> propertyPaths){
641 T bean = findByUuid(uuid);
642 if(bean == null){
643 return bean;
644 }
645 defaultBeanInitializer.initialize(bean, propertyPaths);
646
647 return bean;
648 }
649
650 @Override
651 public Boolean exists(UUID uuid) {
652 if (findByUuid(uuid)==null){
653 return false;
654 }
655 return true;
656 }
657
658 @Override
659 public int count() {
660 return count(type);
661 }
662
663 @Override
664 public int count(Class<? extends T> clazz) {
665 Session session = getSession();
666 Criteria criteria = null;
667 if(clazz == null) {
668 criteria = session.createCriteria(type);
669 } else {
670 criteria = session.createCriteria(clazz);
671 }
672 criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
673
674 //since hibernate 4 (or so) uniqueResult returns Long, not Integer, therefore needs
675 //to be casted. Think about returning long rather then int!
676 return ((Number) criteria.uniqueResult()).intValue();
677 }
678
679 @Override
680 public List<T> list(Integer limit, Integer start) {
681 return list(limit, start, null);
682 }
683
684 @Override
685 public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {
686
687 Criteria criteria = null;
688 criteria = criterionForType(clazz);
689
690 addGroups(criteria,groups);
691
692 if(limit != null) {
693 criteria.setFirstResult(start);
694 criteria.setMaxResults(limit);
695 }
696
697 @SuppressWarnings("unchecked")
698 List<Object[]> result = criteria.list();
699
700 if(propertyPaths != null && !propertyPaths.isEmpty()) {
701 for(Object[] objects : result) {
702 defaultBeanInitializer.initialize(objects[0], propertyPaths);
703 }
704 }
705
706 return result;
707 }
708
709 protected void countGroups(DetachedCriteria criteria,List<Grouping> groups) {
710 if(groups != null){
711
712
713 Map<String,String> aliases = new HashMap<String,String>();
714
715 for(Grouping grouping : groups) {
716 if(grouping.getAssociatedObj() != null) {
717 String alias = null;
718 if((alias = aliases.get(grouping.getAssociatedObj())) == null) {
719 alias = grouping.getAssociatedObjectAlias();
720 aliases.put(grouping.getAssociatedObj(), alias);
721 criteria.createAlias(grouping.getAssociatedObj(),alias);
722 }
723 }
724 }
725
726 ProjectionList projectionList = Projections.projectionList();
727
728 for(Grouping grouping : groups) {
729 grouping.addProjection(projectionList);
730 }
731 criteria.setProjection(projectionList);
732 }
733 }
734
735 protected void addGroups(Criteria criteria,List<Grouping> groups) {
736 if(groups != null){
737
738
739 Map<String,String> aliases = new HashMap<String,String>();
740
741 for(Grouping grouping : groups) {
742 if(grouping.getAssociatedObj() != null) {
743 String alias = null;
744 if((alias = aliases.get(grouping.getAssociatedObj())) == null) {
745 alias = grouping.getAssociatedObjectAlias();
746 aliases.put(grouping.getAssociatedObj(), alias);
747 criteria.createAlias(grouping.getAssociatedObj(),alias);
748 }
749 }
750 }
751
752 ProjectionList projectionList = Projections.projectionList();
753
754 for(Grouping grouping : groups) {
755 grouping.addProjection(projectionList);
756 }
757 criteria.setProjection(projectionList);
758
759 for(Grouping grouping : groups) {
760 grouping.addOrder(criteria);
761
762 }
763 }
764 }
765
766 protected void addCriteria(Criteria criteria, List<Criterion> criterion) {
767 if(criterion != null) {
768 for(Criterion c : criterion) {
769 criteria.add(c);
770 }
771 }
772
773 }
774
775 protected void addOrder(FullTextQuery fullTextQuery, List<OrderHint> orderHints) {
776 //FIXME preliminary hardcoded type:
777 SortField.Type type = SortField.Type.STRING;
778
779 if(orderHints != null && !orderHints.isEmpty()) {
780 org.apache.lucene.search.Sort sort = new Sort();
781 SortField[] sortFields = new SortField[orderHints.size()];
782 for(int i = 0; i < orderHints.size(); i++) {
783 OrderHint orderHint = orderHints.get(i);
784 switch(orderHint.getSortOrder()) {
785 case ASCENDING:
786 sortFields[i] = new SortField(orderHint.getPropertyName(), type, true);
787 break;
788 case DESCENDING:
789 default:
790 sortFields[i] = new SortField(orderHint.getPropertyName(), type, false);
791
792 }
793 }
794 sort.setSort(sortFields);
795 fullTextQuery.setSort(sort);
796
797 }
798 }
799
800 @Override
801 public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints) {
802 return list(limit,start,orderHints,null);
803 }
804
805 @Override
806 public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
807 Criteria criteria = getSession().createCriteria(type);
808 if(limit != null) {
809 criteria.setFirstResult(start);
810 criteria.setMaxResults(limit);
811 }
812
813 addOrder(criteria,orderHints);
814 @SuppressWarnings("unchecked")
815 List<T> results = criteria.list();
816
817 defaultBeanInitializer.initializeAll(results, propertyPaths);
818 return results;
819 }
820
821
822 @Override
823 public <S extends T> List<S> list(Class<S> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
824 Criteria criteria = null;
825 if(clazz == null) {
826 criteria = getSession().createCriteria(type);
827 } else {
828 criteria = getSession().createCriteria(clazz);
829 }
830
831 addLimitAndStart(limit, start, criteria);
832
833 addOrder(criteria, orderHints);
834
835 @SuppressWarnings("unchecked")
836 List<S> results = criteria.list();
837
838 defaultBeanInitializer.initializeAll(results, propertyPaths);
839 return results;
840 }
841
842 /**
843 * @param limit
844 * @param start
845 * @param criteria
846 */
847 private void addLimitAndStart(Integer limit, Integer start, Criteria criteria) {
848 if(limit != null) {
849 if(start != null) {
850 criteria.setFirstResult(start);
851 } else {
852 criteria.setFirstResult(0);
853 }
854 criteria.setMaxResults(limit);
855 }
856 }
857
858
859 public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start, List<OrderHint> orderHints) {
860 return list(type,limit,start,orderHints,null);
861 }
862
863 @Override
864 public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start) {
865 return list(type,limit,start,null,null);
866 }
867
868 @Override
869 public List<T> rows(String tableName, int limit, int start) {
870 Query query = getSession().createQuery("from " + tableName + " order by uuid");
871 query.setFirstResult(start);
872 query.setMaxResults(limit);
873 @SuppressWarnings("unchecked")
874 List<T> result = query.list();
875 return result;
876 }
877
878 @Override
879 public Class<T> getType() {
880 return type;
881 }
882
883 protected void setPagingParameter(Query query, Integer pageSize, Integer pageNumber){
884 if(pageSize != null) {
885 query.setMaxResults(pageSize);
886 if(pageNumber != null) {
887 query.setFirstResult(pageNumber * pageSize);
888 } else {
889 query.setFirstResult(0);
890 }
891 }
892 }
893
894 protected void setPagingParameter(AuditQuery query, Integer pageSize, Integer pageNumber){
895 if(pageSize != null) {
896 query.setMaxResults(pageSize);
897 if(pageNumber != null) {
898 query.setFirstResult(pageNumber * pageSize);
899 } else {
900 query.setFirstResult(0);
901 }
902 }
903 }
904
905 @Override
906 public int count(T example, Set<String> includeProperties) {
907 Criteria criteria = getSession().createCriteria(example.getClass());
908 addExample(criteria,example,includeProperties);
909
910 criteria.setProjection(Projections.rowCount());
911 return ((Number)criteria.uniqueResult()).intValue();
912 }
913
914 protected void addExample(Criteria criteria, T example, Set<String> includeProperties) {
915 if(includeProperties != null && !includeProperties.isEmpty()) {
916 criteria.add(Example.create(example).setPropertySelector(new PropertySelectorImpl(includeProperties)));
917 ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(example.getClass());
918 for(String property : includeProperties) {
919 Type type = classMetadata.getPropertyType(property);
920 if(type.isEntityType()) {
921 try {
922 Field field = ReflectionUtils.findField(example.getClass(), property);
923 field.setAccessible(true);
924 Object value = field.get(example);
925 if(value != null) {
926 criteria.add(Restrictions.eq(property,value));
927 } else {
928 criteria.add(Restrictions.isNull(property));
929 }
930 } catch (SecurityException se) {
931 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, se);
932 } catch (HibernateException he) {
933 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, he);
934 } catch (IllegalArgumentException iae) {
935 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, iae);
936 } catch (IllegalAccessException ie) {
937 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, ie);
938 }
939
940 }
941 }
942 } else {
943 criteria.add(Example.create(example));
944 }
945 }
946
947
948 protected long countByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode, List<Criterion> criterion) {
949 Criteria criteria = null;
950
951 criteria = criterionForType(clazz);
952
953 if (queryString != null) {
954 if(matchmode == null) {
955 criteria.add(Restrictions.ilike(param, queryString));
956 } else if(matchmode == MatchMode.BEGINNING) {
957 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));
958 } else if(matchmode == MatchMode.END) {
959 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));
960 } else if(matchmode == MatchMode.EXACT) {
961 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));
962 } else {
963 criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));
964 }
965 }
966
967 addCriteria(criteria, criterion);
968
969 criteria.setProjection(Projections.rowCount());
970
971 return ((Number)criteria.uniqueResult()).longValue();
972 }
973
974
975 @Override
976 public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
977 Criteria criteria = getSession().createCriteria(example.getClass());
978 addExample(criteria,example,includeProperties);
979
980 addLimitAndStart(limit, start, criteria);
981
982 addOrder(criteria,orderHints);
983
984 @SuppressWarnings("unchecked")
985 List<T> results = criteria.list();
986 defaultBeanInitializer.initializeAll(results, propertyPaths);
987 return results;
988 }
989
990 private class PropertySelectorImpl implements PropertySelector {
991
992 private final Set<String> includeProperties;
993 /**
994 *
995 */
996 private static final long serialVersionUID = -3175311800911570546L;
997
998 public PropertySelectorImpl(Set<String> includeProperties) {
999 this.includeProperties = includeProperties;
1000 }
1001
1002 @Override
1003 public boolean include(Object propertyValue, String propertyName, Type type) {
1004 if(includeProperties.contains(propertyName)) {
1005 return true;
1006 } else {
1007 return false;
1008 }
1009 }
1010
1011 }
1012 }
1013