2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.common
;
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
;
21 import java
.util
.UUID
;
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
;
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
;
70 * FIXME CdmEntityDaoBase is abstract, can it be annotated with @Repository?
73 public abstract class CdmEntityDaoBase
<T
extends CdmBase
> extends DaoBase
implements ICdmEntityDao
<T
> {
75 private static final Logger logger
= Logger
.getLogger(CdmEntityDaoBase
.class);
77 protected int flushAfterNo
= 1000; //large numbers may cause synchronisation errors when commiting the session !!
79 protected Class
<T
> type
;
81 // protected Version version = Configuration.luceneVersion;
84 // @Qualifier("defaultBeanInitializer")
85 protected IBeanInitializer defaultBeanInitializer
;
87 public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer
) {
88 this.defaultBeanInitializer
= defaultBeanInitializer
;
92 private ReferringObjectMetadataFactory referringObjectMetadataFactory
;
95 public CdmEntityDaoBase(Class
<T
> type
){
97 logger
.debug("Creating DAO of type [" + type
.getSimpleName() + "]");
101 public void lock(T t
, LockOptions lockOptions
) {
102 getSession().buildLockRequest(lockOptions
).lock(t
);
106 public void refresh(T t
, LockOptions lockOptions
, List
<String
> propertyPaths
) {
107 getSession().refresh(t
, lockOptions
);
108 defaultBeanInitializer
.initialize(t
, propertyPaths
);
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();
117 //TODO: Replace saveCdmObj() by saveCdmObject_
118 private UUID
saveCdmObject_(T cdmObj
){
119 getSession().saveOrUpdate(cdmObj
);
120 return cdmObj
.getUuid();
123 //TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of ServiceBase.saveCdmObjectAll()?
124 //TODO: why does this use saveCdmObject_ which actually savesOrUpdateds data ?
126 public Map
<UUID
, T
> saveAll(Collection
<T
> cdmObjCollection
){
127 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
129 if (logger
.isDebugEnabled()){logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);}
132 Map
<UUID
, T
> resultMap
= new HashMap
<UUID
, T
>();
133 Iterator
<T
> iterator
= cdmObjCollection
.iterator();
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
);
142 if ( (i
% flushAfterNo
) == 0){
144 if (logger
.isDebugEnabled()){logger
.debug("flush");}
147 logger
.error("An exception occurred when trying to flush data");
149 throw new RuntimeException(e
);
154 if ( logger
.isInfoEnabled() ){logger
.info("Saved " + i
+ " objects" );}
158 private UUID
saveOrUpdateCdmObject(T cdmObj
){
159 getSession().saveOrUpdate(cdmObj
);
160 return cdmObj
.getUuid();
164 public Map
<UUID
, T
> saveOrUpdateAll(Collection
<T
> cdmObjCollection
){
165 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
167 if (logger
.isDebugEnabled()){logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);}
170 Map
<UUID
, T
> resultMap
= new HashMap
<>();
171 Iterator
<T
> iterator
= cdmObjCollection
.iterator();
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
);
180 if ( (i
% flushAfterNo
) == 0){
182 if (logger
.isDebugEnabled()){logger
.debug("flush");}
185 logger
.error("An exception occurred when trying to flush data");
187 throw new RuntimeException(e
);
192 if ( logger
.isInfoEnabled() ){logger
.info("Saved " + i
+ " objects" );}
200 public T
replace(T x
, T y
) {
205 Class
<?
> commonClass
= x
.getClass();
207 while(!commonClass
.isAssignableFrom(y
.getClass())) {
208 if(commonClass
.equals(type
)) {
209 throw new RuntimeException();
211 commonClass
= commonClass
.getSuperclass();
215 getSession().merge(x
);
217 Set
<ReferringObjectMetadata
> referringObjectMetas
= referringObjectMetadataFactory
.get(x
.getClass());
219 for(ReferringObjectMetadata referringObjectMetadata
: referringObjectMetas
) {
221 List
<CdmBase
> referringObjects
= referringObjectMetadata
.getReferringObjects(x
, getSession());
223 for(CdmBase referringObject
: referringObjects
) {
225 referringObjectMetadata
.replace(referringObject
,x
,y
);
226 getSession().update(referringObject
);
228 } catch (IllegalArgumentException e
) {
229 throw new RuntimeException(e
.getMessage(),e
);
230 } catch (IllegalAccessException e
) {
231 throw new RuntimeException(e
.getMessage(),e
);
239 public Session
getSession() throws DataAccessException
{
240 return super.getSession();
244 public void clear() throws DataAccessException
{
245 Session session
= getSession();
247 if (logger
.isDebugEnabled()){logger
.debug("dao clear end");}
251 public MergeResult
<T
> merge(T transientObject
, boolean returnTransientEntity
) throws DataAccessException
{
252 Session session
= getSession();
253 PostMergeEntityListener
.addSession(session
);
254 MergeResult
<T
> result
= null;
256 @SuppressWarnings("unchecked")
257 T persistentObject
= (T
)session
.merge(transientObject
);
258 if (logger
.isDebugEnabled()){logger
.debug("dao merge end");}
260 if(returnTransientEntity
) {
261 if(transientObject
!= null && persistentObject
!= null) {
262 transientObject
.setId(persistentObject
.getId());
264 result
= new MergeResult(transientObject
, PostMergeEntityListener
.getNewEntities(session
));
266 result
= new MergeResult(persistentObject
, null);
270 PostMergeEntityListener
.removeSession(session
);
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
;
284 public UUID
saveOrUpdate(T transientObject
) throws DataAccessException
{
285 if (transientObject
== null){
286 logger
.warn("Object to save should not be null. NOP");
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
);
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());
313 } catch (HibernateException e
) {
321 public T
save(T newInstance
) throws DataAccessException
{
322 if (newInstance
== null){
323 logger
.warn("Object to save should not be null. NOP");
326 getSession().save(newInstance
);
331 public UUID
update(T transientObject
) throws DataAccessException
{
332 if (transientObject
== null){
333 logger
.warn("Object to update should not be null. NOP");
336 getSession().update(transientObject
);
337 return transientObject
.getUuid();
341 public UUID
refresh(T persistentObject
) throws DataAccessException
{
342 getSession().refresh(persistentObject
);
343 return persistentObject
.getUuid();
347 public UUID
delete(T persistentObject
) throws DataAccessException
{
348 if (persistentObject
== null){
349 logger
.warn(type
.getName() + " was 'null'");
353 // Merge the object in if it is detached
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();
364 public T
findById(int id
) throws DataAccessException
{
365 return getSession().get(type
, id
);
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()){
382 if(resultSet
.size() > 1){
383 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
385 return results
.get(0);
390 public T
findByUuidWithoutFlush(UUID uuid
) throws DataAccessException
{
391 Session session
= getSession();
392 FlushMode currentFlushMode
= session
.getFlushMode();
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()){
405 if(results
.size() > 1){
406 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
408 return results
.get(0);
411 // set back the session flush mode
412 if(currentFlushMode
!= null) {
413 session
.setFlushMode(currentFlushMode
);
419 public List
<T
> loadList(Collection
<Integer
> ids
, List
<String
> propertyPaths
) throws DataAccessException
{
422 return new ArrayList
<T
>(0);
425 Criteria criteria
= prepareList(ids
, null, null, null, "id");
427 if (logger
.isDebugEnabled()){logger
.debug(criteria
.toString());}
429 @SuppressWarnings("unchecked")
430 List
<T
> result
= criteria
.list();
431 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
437 public List
<T
> list(Collection
<UUID
> uuids
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws DataAccessException
{
439 if (uuids
== null || uuids
.isEmpty()){
440 return new ArrayList
<>();
443 Criteria criteria
= prepareList(uuids
, pageSize
, pageNumber
, orderHints
, "uuid");
445 @SuppressWarnings("unchecked")
446 List
<T
> result
= criteria
.list();
447 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
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
) {
458 Criteria criteria
= criterionForType(type
);
460 if (propertyName
!= null) {
461 addRestriction(propertyName
, value
, matchMode
, criteria
);
464 addLimitAndStart(limit
, start
, criteria
);
465 addOrder(criteria
, orderHints
);
467 @SuppressWarnings("unchecked")
468 List
<T
> result
= criteria
.list();
469 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
474 * @param propertyName
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
);
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
);
496 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
);
499 criteria
.add(restriction
);
506 public int count(Class
<?
extends T
> type
, String propertyName
, Object value
, MatchMode matchMode
) {
508 Criteria criteria
= criterionForType(type
);
510 if (propertyName
!= null) {
511 addRestriction(propertyName
, value
, matchMode
, criteria
);
514 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
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();
527 * @param propertyName
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
));
534 if(pageSize
!= null) {
535 criteria
.setMaxResults(pageSize
);
536 if(pageNumber
!= null) {
537 criteria
.setFirstResult(pageNumber
* pageSize
);
539 criteria
.setFirstResult(0);
543 if(orderHints
== null) {
544 orderHints
= OrderHint
.defaultOrderHintsFor(type
);
546 addOrder(criteria
, orderHints
);
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.
563 * @param propertyPaths
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
) {
568 Criteria criteria
= criterionForType(clazz
);
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
));
580 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
));
584 addCriteria(criteria
, criterion
);
586 if(pageSize
!= null) {
587 criteria
.setMaxResults(pageSize
);
588 if(pageNumber
!= null) {
589 criteria
.setFirstResult(pageNumber
* pageSize
);
591 criteria
.setFirstResult(0);
595 addOrder(criteria
, orderHints
);
597 @SuppressWarnings("unchecked")
598 List
<T
> result
= criteria
.list();
599 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
607 private Criteria
criterionForType(Class
<?
extends T
> clazz
) {
610 criteria
= getSession().createCriteria(type
);
612 criteria
= getSession().createCriteria(clazz
);
618 public T
load(UUID uuid
) {
619 T bean
= findByUuid(uuid
);
623 defaultBeanInitializer
.load(bean
);
629 public T
load(int id
, List
<String
> propertyPaths
){
630 T bean
= findById(id
);
634 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
640 public T
load(UUID uuid
, List
<String
> propertyPaths
){
641 T bean
= findByUuid(uuid
);
645 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
651 public Boolean
exists(UUID uuid
) {
652 if (findByUuid(uuid
)==null){
664 public int count(Class
<?
extends T
> clazz
) {
665 Session session
= getSession();
666 Criteria criteria
= null;
668 criteria
= session
.createCriteria(type
);
670 criteria
= session
.createCriteria(clazz
);
672 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
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();
680 public List
<T
> list(Integer limit
, Integer start
) {
681 return list(limit
, start
, null);
685 public List
<Object
[]> group(Class
<?
extends T
> clazz
,Integer limit
, Integer start
, List
<Grouping
> groups
, List
<String
> propertyPaths
) {
687 Criteria criteria
= null;
688 criteria
= criterionForType(clazz
);
690 addGroups(criteria
,groups
);
693 criteria
.setFirstResult(start
);
694 criteria
.setMaxResults(limit
);
697 @SuppressWarnings("unchecked")
698 List
<Object
[]> result
= criteria
.list();
700 if(propertyPaths
!= null && !propertyPaths
.isEmpty()) {
701 for(Object
[] objects
: result
) {
702 defaultBeanInitializer
.initialize(objects
[0], propertyPaths
);
709 protected void countGroups(DetachedCriteria criteria
,List
<Grouping
> groups
) {
713 Map
<String
,String
> aliases
= new HashMap
<String
,String
>();
715 for(Grouping grouping
: groups
) {
716 if(grouping
.getAssociatedObj() != 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
);
726 ProjectionList projectionList
= Projections
.projectionList();
728 for(Grouping grouping
: groups
) {
729 grouping
.addProjection(projectionList
);
731 criteria
.setProjection(projectionList
);
735 protected void addGroups(Criteria criteria
,List
<Grouping
> groups
) {
739 Map
<String
,String
> aliases
= new HashMap
<String
,String
>();
741 for(Grouping grouping
: groups
) {
742 if(grouping
.getAssociatedObj() != 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
);
752 ProjectionList projectionList
= Projections
.projectionList();
754 for(Grouping grouping
: groups
) {
755 grouping
.addProjection(projectionList
);
757 criteria
.setProjection(projectionList
);
759 for(Grouping grouping
: groups
) {
760 grouping
.addOrder(criteria
);
766 protected void addCriteria(Criteria criteria
, List
<Criterion
> criterion
) {
767 if(criterion
!= null) {
768 for(Criterion c
: criterion
) {
775 protected void addOrder(FullTextQuery fullTextQuery
, List
<OrderHint
> orderHints
) {
776 //FIXME preliminary hardcoded type:
777 SortField
.Type type
= SortField
.Type
.STRING
;
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()) {
786 sortFields
[i
] = new SortField(orderHint
.getPropertyName(), type
, true);
790 sortFields
[i
] = new SortField(orderHint
.getPropertyName(), type
, false);
794 sort
.setSort(sortFields
);
795 fullTextQuery
.setSort(sort
);
801 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
802 return list(limit
,start
,orderHints
,null);
806 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
807 Criteria criteria
= getSession().createCriteria(type
);
809 criteria
.setFirstResult(start
);
810 criteria
.setMaxResults(limit
);
813 addOrder(criteria
,orderHints
);
814 @SuppressWarnings("unchecked")
815 List
<T
> results
= criteria
.list();
817 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
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;
826 criteria
= getSession().createCriteria(type
);
828 criteria
= getSession().createCriteria(clazz
);
831 addLimitAndStart(limit
, start
, criteria
);
833 addOrder(criteria
, orderHints
);
835 @SuppressWarnings("unchecked")
836 List
<S
> results
= criteria
.list();
838 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
847 private void addLimitAndStart(Integer limit
, Integer start
, Criteria criteria
) {
850 criteria
.setFirstResult(start
);
852 criteria
.setFirstResult(0);
854 criteria
.setMaxResults(limit
);
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);
864 public <S
extends T
> List
<S
> list(Class
<S
> type
, Integer limit
, Integer start
) {
865 return list(type
,limit
,start
,null,null);
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();
879 public Class
<T
> getType() {
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
);
889 query
.setFirstResult(0);
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
);
900 query
.setFirstResult(0);
906 public int count(T example
, Set
<String
> includeProperties
) {
907 Criteria criteria
= getSession().createCriteria(example
.getClass());
908 addExample(criteria
,example
,includeProperties
);
910 criteria
.setProjection(Projections
.rowCount());
911 return ((Number
)criteria
.uniqueResult()).intValue();
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()) {
922 Field field
= ReflectionUtils
.findField(example
.getClass(), property
);
923 field
.setAccessible(true);
924 Object value
= field
.get(example
);
926 criteria
.add(Restrictions
.eq(property
,value
));
928 criteria
.add(Restrictions
.isNull(property
));
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
);
943 criteria
.add(Example
.create(example
));
948 protected long countByParam(Class
<?
extends T
> clazz
, String param
, String queryString
, MatchMode matchmode
, List
<Criterion
> criterion
) {
949 Criteria criteria
= null;
951 criteria
= criterionForType(clazz
);
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
));
963 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
));
967 addCriteria(criteria
, criterion
);
969 criteria
.setProjection(Projections
.rowCount());
971 return ((Number
)criteria
.uniqueResult()).longValue();
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
);
980 addLimitAndStart(limit
, start
, criteria
);
982 addOrder(criteria
,orderHints
);
984 @SuppressWarnings("unchecked")
985 List
<T
> results
= criteria
.list();
986 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
990 private class PropertySelectorImpl
implements PropertySelector
{
992 private final Set
<String
> includeProperties
;
996 private static final long serialVersionUID
= -3175311800911570546L;
998 public PropertySelectorImpl(Set
<String
> includeProperties
) {
999 this.includeProperties
= includeProperties
;
1003 public boolean include(Object propertyValue
, String propertyName
, Type type
) {
1004 if(includeProperties
.contains(propertyName
)) {