Project

General

Profile

Download (37.6 KB) Statistics
| Branch: | Tag: | Revision:
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.common.Restriction;
59
import eu.etaxonomy.cdm.persistence.dao.initializer.IBeanInitializer;
60
import eu.etaxonomy.cdm.persistence.dto.MergeResult;
61
import eu.etaxonomy.cdm.persistence.hibernate.PostMergeEntityListener;
62
import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadata;
63
import eu.etaxonomy.cdm.persistence.hibernate.replace.ReferringObjectMetadataFactory;
64
import eu.etaxonomy.cdm.persistence.query.Grouping;
65
import eu.etaxonomy.cdm.persistence.query.MatchMode;
66
import eu.etaxonomy.cdm.persistence.query.OrderHint;
67

    
68

    
69
/**
70
 * @author a.mueller
71
 * FIXME CdmEntityDaoBase is abstract, can it be annotated with @Repository?
72
 */
73
@Repository
74
public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implements ICdmEntityDao<T> {
75

    
76
    private static final Logger logger = Logger.getLogger(CdmEntityDaoBase.class);
77

    
78
    protected int flushAfterNo = 1000; //large numbers may cause synchronisation errors when commiting the session !!
79

    
80
    protected Class<T> type;
81

    
82
//    protected Version version = Configuration.luceneVersion;
83

    
84
    @Autowired
85
//	@Qualifier("defaultBeanInitializer")
86
    protected IBeanInitializer defaultBeanInitializer;
87

    
88
    public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer) {
89
        this.defaultBeanInitializer = defaultBeanInitializer;
90
    }
91

    
92
    @Autowired
93
    private ReferringObjectMetadataFactory referringObjectMetadataFactory;
94

    
95

    
96
    public CdmEntityDaoBase(Class<T> type){
97
        this.type = type;
98
        logger.debug("Creating DAO of type [" + type.getSimpleName() + "]");
99
    }
100

    
101
    @Override
102
    public void lock(T t, LockOptions lockOptions) {
103
        getSession().buildLockRequest(lockOptions).lock(t);
104
    }
105

    
106
    @Override
107
    public void refresh(T t, LockOptions lockOptions, List<String> propertyPaths) {
108
        getSession().refresh(t, lockOptions);
109
        defaultBeanInitializer.initialize(t, propertyPaths);
110
    }
111

    
112
    //TODO this method should be moved to a concrete class (not typed)
113
    public UUID saveCdmObj(CdmBase cdmObj) throws DataAccessException  {
114
        getSession().saveOrUpdate(cdmObj);
115
        return cdmObj.getUuid();
116
    }
117

    
118
    //TODO: Replace saveCdmObj() by saveCdmObject_
119
    private UUID saveCdmObject_(T cdmObj){
120
        getSession().saveOrUpdate(cdmObj);
121
        return cdmObj.getUuid();
122
    }
123

    
124
    //TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of ServiceBase.saveCdmObjectAll()?
125
    //TODO: why does this use saveCdmObject_ which actually savesOrUpdateds data ?
126
    @Override
127
    public Map<UUID, T> saveAll(Collection<T> cdmObjCollection){
128
        int types = cdmObjCollection.getClass().getTypeParameters().length;
129
        if (types > 0){
130
            if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}
131
        }
132

    
133
        Map<UUID, T> resultMap = new HashMap<>();
134
        Iterator<T> iterator = cdmObjCollection.iterator();
135
        int i = 0;
136
        while(iterator.hasNext()){
137
            if ( ( (i % 2000) == 0) && (i > 0)   ){logger.debug("Saved " + i + " objects" );}
138
            T cdmObj = iterator.next();
139
            UUID uuid = saveCdmObject_(cdmObj);
140
            if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}
141
            resultMap.put(uuid, cdmObj);
142
            i++;
143
            if ( (i % flushAfterNo) == 0){
144
                try{
145
                    if (logger.isDebugEnabled()){logger.debug("flush");}
146
                    flush();
147
                }catch(Exception e){
148
                    logger.error("An exception occurred when trying to flush data");
149
                    e.printStackTrace();
150
                    throw new RuntimeException(e);
151
                }
152
            }
153
        }
154

    
155
        if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}
156
        return resultMap;
157
    }
158

    
159
    private UUID saveOrUpdateCdmObject(T cdmObj){
160
        getSession().saveOrUpdate(cdmObj);
161
        return cdmObj.getUuid();
162
    }
163

    
164
    @Override
165
    public Map<UUID, T> saveOrUpdateAll(Collection<T> cdmObjCollection){
166
        int types = cdmObjCollection.getClass().getTypeParameters().length;
167
        if (types > 0){
168
            if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}
169
        }
170

    
171
        Map<UUID, T> resultMap = new HashMap<>();
172
        Iterator<T> iterator = cdmObjCollection.iterator();
173
        int i = 0;
174
        while(iterator.hasNext()){
175
            if ( ( (i % 2000) == 0) && (i > 0)   ){logger.debug("Saved " + i + " objects" );}
176
            T cdmObj = iterator.next();
177
            UUID uuid = saveOrUpdateCdmObject(cdmObj);
178
            if (logger.isDebugEnabled()){logger.debug("Save cdmObj: " + (cdmObj == null? null: cdmObj.toString()));}
179
            resultMap.put(uuid, cdmObj);
180
            i++;
181
            if ( (i % flushAfterNo) == 0){
182
                try{
183
                    if (logger.isDebugEnabled()){logger.debug("flush");}
184
                    flush();
185
                }catch(Exception e){
186
                    logger.error("An exception occurred when trying to flush data");
187
                    e.printStackTrace();
188
                    throw new RuntimeException(e);
189
                }
190
            }
191
        }
192

    
193
        if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}
194
        return resultMap;
195
    }
196

    
197

    
198

    
199

    
200
    @Override
201
    public T replace(T x, T y) {
202
        if(x.equals(y)) {
203
            return y;
204
        }
205

    
206
        Class<?> commonClass = x.getClass();
207
        if(y != null) {
208
            while(!commonClass.isAssignableFrom(y.getClass())) {
209
                if(commonClass.equals(type)) {
210
                    throw new RuntimeException();
211
                }
212
                commonClass = commonClass.getSuperclass();
213
            }
214
        }
215

    
216
        getSession().merge(x);
217

    
218
        Set<ReferringObjectMetadata> referringObjectMetas = referringObjectMetadataFactory.get(x.getClass());
219

    
220
        for(ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {
221

    
222
          List<CdmBase> referringObjects = referringObjectMetadata.getReferringObjects(x, getSession());
223

    
224
          for(CdmBase referringObject : referringObjects) {
225
            try {
226
                referringObjectMetadata.replace(referringObject,x,y);
227
                getSession().update(referringObject);
228

    
229
            } catch (IllegalArgumentException e) {
230
                throw new RuntimeException(e.getMessage(),e);
231
            } catch (IllegalAccessException e) {
232
                throw new RuntimeException(e.getMessage(),e);
233
            }
234
          }
235
        }
236
        return y;
237
    }
238

    
239
    @Override
240
    public Session getSession() throws DataAccessException {
241
        return super.getSession();
242
    }
243

    
244
    @Override
245
    public void clear() throws DataAccessException {
246
        Session session = getSession();
247
        session.clear();
248
        if (logger.isDebugEnabled()){logger.debug("dao clear end");}
249
    }
250

    
251
    @Override
252
    public MergeResult<T> merge(T transientObject, boolean returnTransientEntity) throws DataAccessException {
253
        Session session = getSession();
254
        PostMergeEntityListener.addSession(session);
255
        MergeResult<T> result = null;
256
        try {
257
            @SuppressWarnings("unchecked")
258
            T persistentObject = (T)session.merge(transientObject);
259
            if (logger.isDebugEnabled()){logger.debug("dao merge end");}
260

    
261
            if(returnTransientEntity) {
262
                if(transientObject != null && persistentObject != null) {
263
                    transientObject.setId(persistentObject.getId());
264
                }
265
                result = new MergeResult(transientObject, PostMergeEntityListener.getNewEntities(session));
266
            } else {
267
                result = new MergeResult(persistentObject, null);
268
            }
269
            return result;
270
        } finally {
271
            PostMergeEntityListener.removeSession(session);
272
        }
273
    }
274

    
275
    @Override
276
    public T merge(T transientObject) throws DataAccessException {
277
        Session session = getSession();
278
        @SuppressWarnings("unchecked")
279
		T persistentObject = (T)session.merge(transientObject);
280
        if (logger.isDebugEnabled()){logger.debug("dao merge end");}
281
        return persistentObject;
282
    }
283

    
284
    @Override
285
    public UUID saveOrUpdate(T transientObject) throws DataAccessException  {
286
        if (transientObject == null){
287
        	logger.warn("Object to save should not be null. NOP");
288
        	return null;
289
        }
290
    	try {
291
            if (logger.isDebugEnabled()){logger.debug("dao saveOrUpdate start...");}
292
            if (logger.isDebugEnabled()){logger.debug("transientObject(" + transientObject.getClass().getSimpleName() + ") ID:" + transientObject.getId() + ", UUID: " + transientObject.getUuid()) ;}
293
            Session session = getSession();
294
            if(transientObject.getId() != 0 && VersionableEntity.class.isAssignableFrom(transientObject.getClass())) {
295
                VersionableEntity versionableEntity = (VersionableEntity)transientObject;
296
                versionableEntity.setUpdated(new DateTime());
297
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
298
                if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
299
                  User user = (User)authentication.getPrincipal();
300
                  versionableEntity.setUpdatedBy(user);
301
                }
302
            }
303
            session.saveOrUpdate(transientObject);
304
            if (logger.isDebugEnabled()){logger.debug("dao saveOrUpdate end");}
305
            return transientObject.getUuid();
306
        } catch (NonUniqueObjectException e) {
307
            logger.error("Error in CdmEntityDaoBase.saveOrUpdate(obj). ID="+e.getIdentifier()+". Class="+e.getEntityName());
308
            logger.error(e.getMessage());
309

    
310
            e.printStackTrace();
311
            throw e;
312
        } catch (HibernateException e) {
313

    
314
            e.printStackTrace();
315
            throw e;
316
        }
317
    }
318

    
319
    @Override
320
    public T save(T newInstance) throws DataAccessException {
321
        if (newInstance == null){
322
        	logger.warn("Object to save should not be null. NOP");
323
        	return null;
324
        }
325
    	getSession().save(newInstance);
326
        return newInstance;
327
    }
328

    
329
    @Override
330
    public UUID update(T transientObject) throws DataAccessException {
331
        if (transientObject == null){
332
        	logger.warn("Object to update should not be null. NOP");
333
        	return null;
334
        }
335
    	getSession().update(transientObject);
336
        return transientObject.getUuid();
337
    }
338

    
339
    @Override
340
    public UUID refresh(T persistentObject) throws DataAccessException {
341
        getSession().refresh(persistentObject);
342
        return persistentObject.getUuid();
343
    }
344

    
345
    @Override
346
    public UUID delete(T persistentObject) throws DataAccessException {
347
        if (persistentObject == null){
348
            logger.warn(type.getName() + " was 'null'");
349
            return null;
350
        }
351

    
352
        // Merge the object in if it is detached
353
        //
354
        // I think this is preferable to catching lazy initialization errors
355
        // as that solution only swallows and hides the exception, but doesn't
356
        // actually solve it.
357
       persistentObject = (T) getSession().merge(persistentObject);
358
        getSession().delete(persistentObject);
359
        return persistentObject.getUuid();
360
    }
361

    
362
	@Override
363
    public T findById(int id) throws DataAccessException {
364
        return getSession().get(type, id);
365
    }
366

    
367

    
368
    @Override
369
    public T findByUuid(UUID uuid) throws DataAccessException{
370
        Session session = getSession();
371
        Criteria crit = session.createCriteria(type);
372
        crit.add(Restrictions.eq("uuid", uuid));
373
        crit.addOrder(Order.desc("created"));
374
        @SuppressWarnings("unchecked")
375
		List<T> results = crit.list();
376
        Set<T> resultSet = new HashSet<>();
377
        resultSet.addAll(results);
378
        if (resultSet.isEmpty()){
379
            return null;
380
        }else{
381
            if(resultSet.size() > 1){
382
                logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
383
            }
384
            return results.get(0);
385
        }
386
    }
387

    
388
    @Override
389
    public T findByUuidWithoutFlush(UUID uuid) throws DataAccessException{
390
    	Session session = getSession();
391
    	FlushMode currentFlushMode = session.getFlushMode();
392
    	try {
393
    		// set flush mode to manual so that the session does not flush
394
    		// when before performing the query
395
    		session.setFlushMode(FlushMode.MANUAL);
396
    		Criteria crit = session.createCriteria(type);
397
    		crit.add(Restrictions.eq("uuid", uuid));
398
    		crit.addOrder(Order.desc("created"));
399
    		@SuppressWarnings("unchecked")
400
			List<T> results = crit.list();
401
    		if (results.isEmpty()){
402
    			return null;
403
    		}else{
404
    			if(results.size() > 1){
405
    				logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
406
    			}
407
    			return results.get(0);
408
    		}
409
    	} finally {
410
    		// set back the session flush mode
411
    		if(currentFlushMode != null) {
412
    			session.setFlushMode(currentFlushMode);
413
    		}
414
    	}
415
    }
416

    
417
    @Override
418
    public List<T> loadList(Collection<Integer> ids,  List<String> propertyPaths) throws DataAccessException {
419

    
420
        if (ids.isEmpty()) {
421
            return new ArrayList<T>(0);
422
        }
423

    
424
        Criteria criteria = prepareList(ids, null, null, null, "id");
425

    
426
        if (logger.isDebugEnabled()){logger.debug(criteria.toString());}
427

    
428
         @SuppressWarnings("unchecked")
429
		List<T> result = criteria.list();
430
         defaultBeanInitializer.initializeAll(result, propertyPaths);
431
         return result;
432
     }
433

    
434

    
435
    @Override
436
    public List<T> list(Collection<UUID> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {
437

    
438
        if (uuids == null || uuids.isEmpty()){
439
            return new ArrayList<>();
440
        }
441

    
442
        Criteria criteria = prepareList(uuids, pageSize, pageNumber, orderHints, "uuid");
443
        @SuppressWarnings("unchecked")
444
		List<T> result = criteria.list();
445
        defaultBeanInitializer.initializeAll(result, propertyPaths);
446
        return result;
447
    }
448

    
449
    /**
450
     * {@inheritDoc}
451
     */
452
    @Override
453
    public  List<T> list(Class<? extends T> type, List<Restriction<?>> restrictions,
454
            Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
455

    
456
        Criteria criteria = criterionForType(type);
457

    
458
        addRestrictions(restrictions, criteria);
459

    
460
        addLimitAndStart(limit, start, criteria);
461
        addOrder(criteria, orderHints);
462

    
463
        @SuppressWarnings("unchecked")
464
        List<T> result = criteria.list();
465
        defaultBeanInitializer.initializeAll(result, propertyPaths);
466
        return result;
467
    }
468

    
469
    /**
470
     * @param restrictions
471
     * @param criteria
472
     */
473
    private void addRestrictions(List<Restriction<?>> restrictions, Criteria criteria) {
474

    
475
        List<Criterion> perProperty = new ArrayList<>(restrictions.size());
476
        Map<String, String> aliases = new HashMap<>();
477

    
478
        for(Restriction<?> propMatchMode : restrictions){
479
            Collection<? extends Object> values = propMatchMode.getValues();
480
            if(values != null && !values.isEmpty()){
481
                Criterion[] predicates = new Criterion[values.size()];
482
                int i = 0;
483
                for(Object v : values){
484
                    String propertyPath = propMatchMode.getPropertyName();
485
                    // create aliases if the propertyName is a dot separated property path
486
                    String[] props =  propertyPath.split("\\.");
487
                    String aliasKey = "";
488
                    String alias = null;
489
                    for(int p = 0; p < props.length -1; p++){
490
                        if(!aliases.containsKey(aliasKey + "_" + props[p])){
491
                            alias = props[p] + aliases.size();
492
                            aliases.put(aliasKey, alias);
493
                            criteria.createAlias(props[p], alias);
494
                        }
495
                    }
496

    
497
                    String propertyName = alias != null ? alias + "." + props[props.length -1] : propertyPath;
498
                    predicates[i++] = createRestriction(propertyName, v, propMatchMode.getMatchMode());
499
                }
500
                perProperty.add(Restrictions.or(predicates));
501
            }
502
        }
503

    
504
        if(!perProperty.isEmpty()){
505
            criteria.add(Restrictions.and(perProperty.toArray(new Criterion[perProperty.size()])));
506
        }
507
    }
508

    
509
    /**
510
     * @param propertyName
511
     * @param value
512
     * @param matchMode
513
     * @param criteria
514
     * @return
515
     */
516
    private Criterion createRestriction(String propertyName, Object value, MatchMode matchMode) {
517

    
518
        Criterion restriction;
519
        if(matchMode == null) {
520
            restriction = Restrictions.eq(propertyName, value);
521
        } else if(value == null) {
522
            restriction = Restrictions.isNull(propertyName);
523
        } else if(!(value instanceof String)) {
524
            restriction = Restrictions.eq(propertyName, value);
525
        } else {
526
            String queryString = (String)value;
527
            if(matchMode == MatchMode.BEGINNING) {
528
                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.START);
529
            } else if(matchMode == MatchMode.END) {
530
                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.END);
531
            } else if(matchMode == MatchMode.EXACT) {
532
                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.EXACT);
533
            } else {
534
                restriction = Restrictions.ilike(propertyName, queryString, org.hibernate.criterion.MatchMode.ANYWHERE);
535
            }
536
        }
537
        return restriction;
538
    }
539

    
540
    /**
541
     * {@inheritDoc}
542
     */
543
    @Override
544
    public int count(Class<? extends T> type, List<Restriction<?>> restrictions) {
545

    
546
        Criteria criteria = criterionForType(type);
547

    
548
        addRestrictions(restrictions, criteria);
549

    
550
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
551

    
552
        //since hibernate 4 (or so) uniqueResult returns Long, not Integer, therefore needs
553
        //to be casted. Think about returning long rather then int!
554
        return ((Number) criteria.uniqueResult()).intValue();
555

    
556
    }
557

    
558
    /**
559
     * @param uuids
560
     * @param pageSize
561
     * @param pageNumber
562
     * @param orderHints
563
     * @param propertyName
564
     * @return
565
     */
566
    private Criteria prepareList(Collection<?> uuids, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, String propertyName) {
567
        Criteria criteria = getSession().createCriteria(type);
568
        criteria.add(Restrictions.in(propertyName, uuids));
569

    
570
        if(pageSize != null) {
571
            criteria.setMaxResults(pageSize);
572
            if(pageNumber != null) {
573
                criteria.setFirstResult(pageNumber * pageSize);
574
            } else {
575
                criteria.setFirstResult(0);
576
            }
577
        }
578

    
579
        if(orderHints == null) {
580
            orderHints = OrderHint.defaultOrderHintsFor(type);
581
        }
582
        addOrder(criteria, orderHints);
583
        return criteria;
584
    }
585

    
586
    /**
587
     *
588
     * NOTE: We can't reuse {@link #list(Class, String, Object, MatchMode, Integer, Integer, List, List)
589
     * here due to different default behavior of the <code>matchmode</code> parameter.
590
     *
591
     * @param clazz
592
     * @param param
593
     * @param queryString
594
     * @param matchmode
595
     * @param criterion
596
     * @param pageSize
597
     * @param pageNumber
598
     * @param orderHints
599
     * @param propertyPaths
600
     * @return
601
     */
602
    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) {
603

    
604
        Criteria criteria = criterionForType(clazz);
605

    
606
        if (queryString != null) {
607
            if(matchmode == null) {
608
                criteria.add(Restrictions.ilike(param, queryString));
609
            } else if(matchmode == MatchMode.BEGINNING) {
610
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));
611
            } else if(matchmode == MatchMode.END) {
612
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));
613
            } else if(matchmode == MatchMode.EXACT) {
614
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));
615
            } else {
616
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));
617
            }
618
        }
619

    
620
        addCriteria(criteria, criterion);
621

    
622
        if(pageSize != null) {
623
            criteria.setMaxResults(pageSize);
624
            if(pageNumber != null) {
625
                criteria.setFirstResult(pageNumber * pageSize);
626
            } else {
627
                criteria.setFirstResult(0);
628
            }
629
        }
630

    
631
        addOrder(criteria, orderHints);
632

    
633
        @SuppressWarnings("unchecked")
634
		List<T> result = criteria.list();
635
        defaultBeanInitializer.initializeAll(result, propertyPaths);
636
        return result;
637
    }
638

    
639
    /**
640
     * @param clazz
641
     * @return
642
     */
643
    private Criteria criterionForType(Class<? extends T> clazz) {
644
        Criteria criteria;
645
        if(clazz == null) {
646
            criteria = getSession().createCriteria(type);
647
        } else {
648
            criteria = getSession().createCriteria(clazz);
649
        }
650
        return criteria;
651
    }
652

    
653
    @Override
654
    public T load(UUID uuid) {
655
        T bean = findByUuid(uuid);
656
        if(bean == null){
657
            return null;
658
        }
659
        defaultBeanInitializer.load(bean);
660

    
661
        return bean;
662
    }
663

    
664
    @Override
665
    public T load(int id, List<String> propertyPaths){
666
        T bean = findById(id);
667
        if(bean == null){
668
            return bean;
669
        }
670
        defaultBeanInitializer.initialize(bean, propertyPaths);
671

    
672
        return bean;
673
    }
674

    
675
    @Override
676
    public T load(UUID uuid, List<String> propertyPaths){
677
        T bean = findByUuid(uuid);
678
        if(bean == null){
679
            return bean;
680
        }
681
        defaultBeanInitializer.initialize(bean, propertyPaths);
682

    
683
        return bean;
684
    }
685

    
686
    @Override
687
    public Boolean exists(UUID uuid) {
688
        if (findByUuid(uuid)==null){
689
            return false;
690
        }
691
        return true;
692
    }
693

    
694
    @Override
695
    public int count() {
696
        return count(type);
697
    }
698

    
699
    @Override
700
    public int count(Class<? extends T> clazz) {
701
        Session session = getSession();
702
        Criteria criteria = null;
703
        if(clazz == null) {
704
            criteria = session.createCriteria(type);
705
        } else {
706
            criteria = session.createCriteria(clazz);
707
        }
708
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
709

    
710
        //since hibernate 4 (or so) uniqueResult returns Long, not Integer, therefore needs
711
        //to be casted. Think about returning long rather then int!
712
        return ((Number) criteria.uniqueResult()).intValue();
713
    }
714

    
715
    @Override
716
    public List<T> list(Integer limit, Integer start) {
717
        return list(limit, start, null);
718
    }
719

    
720
    @Override
721
    public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {
722

    
723
        Criteria criteria = null;
724
        criteria = criterionForType(clazz);
725

    
726
        addGroups(criteria,groups);
727

    
728
        if(limit != null) {
729
            criteria.setFirstResult(start);
730
            criteria.setMaxResults(limit);
731
        }
732

    
733
        @SuppressWarnings("unchecked")
734
		List<Object[]> result = criteria.list();
735

    
736
        if(propertyPaths != null && !propertyPaths.isEmpty()) {
737
          for(Object[] objects : result) {
738
            defaultBeanInitializer.initialize(objects[0], propertyPaths);
739
          }
740
        }
741

    
742
        return result;
743
    }
744

    
745
    protected void countGroups(DetachedCriteria criteria,List<Grouping> groups) {
746
        if(groups != null){
747

    
748

    
749
            Map<String,String> aliases = new HashMap<String,String>();
750

    
751
            for(Grouping grouping : groups) {
752
                if(grouping.getAssociatedObj() != null) {
753
                    String alias = null;
754
                    if((alias = aliases.get(grouping.getAssociatedObj())) == null) {
755
                        alias = grouping.getAssociatedObjectAlias();
756
                        aliases.put(grouping.getAssociatedObj(), alias);
757
                        criteria.createAlias(grouping.getAssociatedObj(),alias);
758
                    }
759
                }
760
            }
761

    
762
            ProjectionList projectionList = Projections.projectionList();
763

    
764
            for(Grouping grouping : groups) {
765
                grouping.addProjection(projectionList);
766
            }
767
            criteria.setProjection(projectionList);
768
        }
769
    }
770

    
771
    protected void addGroups(Criteria criteria,List<Grouping> groups) {
772
        if(groups != null){
773

    
774

    
775
            Map<String,String> aliases = new HashMap<String,String>();
776

    
777
            for(Grouping grouping : groups) {
778
                if(grouping.getAssociatedObj() != null) {
779
                    String alias = null;
780
                    if((alias = aliases.get(grouping.getAssociatedObj())) == null) {
781
                        alias = grouping.getAssociatedObjectAlias();
782
                        aliases.put(grouping.getAssociatedObj(), alias);
783
                        criteria.createAlias(grouping.getAssociatedObj(),alias);
784
                    }
785
                }
786
            }
787

    
788
            ProjectionList projectionList = Projections.projectionList();
789

    
790
            for(Grouping grouping : groups) {
791
                grouping.addProjection(projectionList);
792
            }
793
            criteria.setProjection(projectionList);
794

    
795
            for(Grouping grouping : groups) {
796
                grouping.addOrder(criteria);
797

    
798
            }
799
        }
800
    }
801

    
802
    protected void addCriteria(Criteria criteria, List<Criterion> criterion) {
803
        if(criterion != null) {
804
            for(Criterion c : criterion) {
805
                criteria.add(c);
806
            }
807
        }
808

    
809
    }
810

    
811
    protected void addOrder(FullTextQuery fullTextQuery, List<OrderHint> orderHints) {
812
        //FIXME preliminary hardcoded type:
813
    	SortField.Type type = SortField.Type.STRING;
814

    
815
    	if(orderHints != null && !orderHints.isEmpty()) {
816
            org.apache.lucene.search.Sort sort = new Sort();
817
            SortField[] sortFields = new SortField[orderHints.size()];
818
            for(int i = 0; i < orderHints.size(); i++) {
819
                OrderHint orderHint = orderHints.get(i);
820
                switch(orderHint.getSortOrder()) {
821
                case ASCENDING:
822
                    sortFields[i] = new SortField(orderHint.getPropertyName(), type, true);
823
                    break;
824
                case DESCENDING:
825
                default:
826
                    sortFields[i] = new SortField(orderHint.getPropertyName(), type, false);
827

    
828
                }
829
            }
830
            sort.setSort(sortFields);
831
            fullTextQuery.setSort(sort);
832

    
833
        }
834
    }
835

    
836
    @Override
837
    public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints) {
838
        return list(limit,start,orderHints,null);
839
    }
840

    
841
    @Override
842
    public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
843
        Criteria criteria = getSession().createCriteria(type);
844
        if(limit != null) {
845
            criteria.setFirstResult(start);
846
            criteria.setMaxResults(limit);
847
        }
848

    
849
        addOrder(criteria,orderHints);
850
        @SuppressWarnings("unchecked")
851
		List<T> results = criteria.list();
852

    
853
        defaultBeanInitializer.initializeAll(results, propertyPaths);
854
        return results;
855
    }
856

    
857

    
858
    @Override
859
    public <S extends T> List<S> list(Class<S> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
860
        Criteria criteria = null;
861
        if(clazz == null) {
862
            criteria = getSession().createCriteria(type);
863
        } else {
864
            criteria = getSession().createCriteria(clazz);
865
        }
866

    
867
        addLimitAndStart(limit, start, criteria);
868

    
869
        addOrder(criteria, orderHints);
870

    
871
        @SuppressWarnings("unchecked")
872
		List<S> results = criteria.list();
873

    
874
        defaultBeanInitializer.initializeAll(results, propertyPaths);
875
        return results;
876
    }
877

    
878
    /**
879
     * @param limit
880
     * @param start
881
     * @param criteria
882
     */
883
    protected void addLimitAndStart(Integer limit, Integer start, Criteria criteria) {
884
        if(limit != null) {
885
            if(start != null) {
886
                criteria.setFirstResult(start);
887
            } else {
888
                criteria.setFirstResult(0);
889
            }
890
            criteria.setMaxResults(limit);
891
        }
892
    }
893

    
894

    
895
    public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start, List<OrderHint> orderHints) {
896
        return list(type,limit,start,orderHints,null);
897
    }
898

    
899
    @Override
900
    public <S extends T> List<S> list(Class<S> type, Integer limit, Integer start) {
901
        return list(type,limit,start,null,null);
902
    }
903

    
904
    @Override
905
    public List<T> rows(String tableName, int limit, int start) {
906
        Query query = getSession().createQuery("from " + tableName + " order by uuid");
907
        query.setFirstResult(start);
908
        query.setMaxResults(limit);
909
        @SuppressWarnings("unchecked")
910
		List<T> result = query.list();
911
        return result;
912
    }
913

    
914
    @Override
915
    public Class<T> getType() {
916
        return type;
917
    }
918

    
919
    protected void setPagingParameter(Query query, Integer pageSize, Integer pageNumber){
920
        if(pageSize != null) {
921
            query.setMaxResults(pageSize);
922
            if(pageNumber != null) {
923
                query.setFirstResult(pageNumber * pageSize);
924
            } else {
925
                query.setFirstResult(0);
926
            }
927
        }
928
    }
929

    
930
    protected void setPagingParameter(AuditQuery query, Integer pageSize, Integer pageNumber){
931
        if(pageSize != null) {
932
            query.setMaxResults(pageSize);
933
            if(pageNumber != null) {
934
                query.setFirstResult(pageNumber * pageSize);
935
            } else {
936
                query.setFirstResult(0);
937
            }
938
        }
939
    }
940

    
941
    @Override
942
    public int count(T example, Set<String> includeProperties) {
943
        Criteria criteria = getSession().createCriteria(example.getClass());
944
        addExample(criteria,example,includeProperties);
945

    
946
        criteria.setProjection(Projections.rowCount());
947
        return ((Number)criteria.uniqueResult()).intValue();
948
    }
949

    
950
    protected void addExample(Criteria criteria, T example, Set<String> includeProperties) {
951
        if(includeProperties != null && !includeProperties.isEmpty()) {
952
            criteria.add(Example.create(example).setPropertySelector(new PropertySelectorImpl(includeProperties)));
953
            ClassMetadata classMetadata = getSession().getSessionFactory().getClassMetadata(example.getClass());
954
            for(String property : includeProperties) {
955
                Type type  = classMetadata.getPropertyType(property);
956
                if(type.isEntityType()) {
957
                    try {
958
                        Field field = ReflectionUtils.findField(example.getClass(), property);
959
                        field.setAccessible(true);
960
                        Object value =  field.get(example);
961
                        if(value != null) {
962
                            criteria.add(Restrictions.eq(property,value));
963
                        } else {
964
                            criteria.add(Restrictions.isNull(property));
965
                        }
966
                    } catch (SecurityException se) {
967
                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, se);
968
                    } catch (HibernateException he) {
969
                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, he);
970
                    } catch (IllegalArgumentException iae) {
971
                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, iae);
972
                    } catch (IllegalAccessException ie) {
973
                        throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property, ie);
974
                    }
975

    
976
                }
977
            }
978
        } else {
979
            criteria.add(Example.create(example));
980
        }
981
    }
982

    
983

    
984
    protected long countByParam(Class<? extends T> clazz, String param, String queryString, MatchMode matchmode, List<Criterion> criterion) {
985
        Criteria criteria = null;
986

    
987
        criteria = criterionForType(clazz);
988

    
989
        if (queryString != null) {
990
            if(matchmode == null) {
991
                criteria.add(Restrictions.ilike(param, queryString));
992
            } else if(matchmode == MatchMode.BEGINNING) {
993
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.START));
994
            } else if(matchmode == MatchMode.END) {
995
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.END));
996
            } else if(matchmode == MatchMode.EXACT) {
997
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.EXACT));
998
            } else {
999
                criteria.add(Restrictions.ilike(param, queryString, org.hibernate.criterion.MatchMode.ANYWHERE));
1000
            }
1001
        }
1002

    
1003
        addCriteria(criteria, criterion);
1004

    
1005
        criteria.setProjection(Projections.rowCount());
1006

    
1007
        return ((Number)criteria.uniqueResult()).longValue();
1008
    }
1009

    
1010

    
1011
    @Override
1012
    public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1013
        Criteria criteria = getSession().createCriteria(example.getClass());
1014
        addExample(criteria,example,includeProperties);
1015

    
1016
        addLimitAndStart(limit, start, criteria);
1017

    
1018
        addOrder(criteria,orderHints);
1019

    
1020
        @SuppressWarnings("unchecked")
1021
		List<T> results = criteria.list();
1022
        defaultBeanInitializer.initializeAll(results, propertyPaths);
1023
        return results;
1024
    }
1025

    
1026
    private class PropertySelectorImpl implements PropertySelector {
1027

    
1028
        private final Set<String> includeProperties;
1029
        /**
1030
         *
1031
         */
1032
        private static final long serialVersionUID = -3175311800911570546L;
1033

    
1034
        public PropertySelectorImpl(Set<String> includeProperties) {
1035
            this.includeProperties = includeProperties;
1036
        }
1037

    
1038
        @Override
1039
        public boolean include(Object propertyValue, String propertyName,	Type type) {
1040
            if(includeProperties.contains(propertyName)) {
1041
                return true;
1042
            } else {
1043
                return false;
1044
            }
1045
        }
1046

    
1047
    }
1048
}
1049

    
(3-3/24)