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
.hibernate
.Criteria
;
25 import org
.hibernate
.FlushMode
;
26 import org
.hibernate
.HibernateException
;
27 import org
.hibernate
.LockOptions
;
28 import org
.hibernate
.NonUniqueObjectException
;
29 import org
.hibernate
.Query
;
30 import org
.hibernate
.Session
;
31 import org
.hibernate
.criterion
.Criterion
;
32 import org
.hibernate
.criterion
.DetachedCriteria
;
33 import org
.hibernate
.criterion
.Example
;
34 import org
.hibernate
.criterion
.Example
.PropertySelector
;
35 import org
.hibernate
.criterion
.LogicalExpression
;
36 import org
.hibernate
.criterion
.Order
;
37 import org
.hibernate
.criterion
.ProjectionList
;
38 import org
.hibernate
.criterion
.Projections
;
39 import org
.hibernate
.criterion
.Restrictions
;
40 import org
.hibernate
.criterion
.Subqueries
;
41 import org
.hibernate
.envers
.AuditReader
;
42 import org
.hibernate
.envers
.AuditReaderFactory
;
43 import org
.hibernate
.envers
.query
.AuditQuery
;
44 import org
.hibernate
.metadata
.ClassMetadata
;
45 import org
.hibernate
.sql
.JoinType
;
46 import org
.hibernate
.type
.Type
;
47 import org
.joda
.time
.DateTime
;
48 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
49 import org
.springframework
.dao
.DataAccessException
;
50 import org
.springframework
.dao
.InvalidDataAccessApiUsageException
;
51 import org
.springframework
.security
.core
.Authentication
;
52 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
53 import org
.springframework
.stereotype
.Repository
;
54 import org
.springframework
.util
.ReflectionUtils
;
56 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
57 import eu
.etaxonomy
.cdm
.model
.common
.IPublishable
;
58 import eu
.etaxonomy
.cdm
.model
.common
.User
;
59 import eu
.etaxonomy
.cdm
.model
.common
.VersionableEntity
;
60 import eu
.etaxonomy
.cdm
.model
.view
.AuditEvent
;
61 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmEntityDao
;
62 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
63 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
.Operator
;
64 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
65 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
66 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.PostMergeEntityListener
;
67 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.replace
.ReferringObjectMetadata
;
68 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.replace
.ReferringObjectMetadataFactory
;
69 import eu
.etaxonomy
.cdm
.persistence
.query
.Grouping
;
70 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
71 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
74 * @author a.mueller FIXME CdmEntityDaoBase is abstract, can it be annotated
78 public abstract class CdmEntityDaoBase
<T
extends CdmBase
> extends DaoBase
implements ICdmEntityDao
<T
> {
80 private static final Logger logger
= Logger
.getLogger(CdmEntityDaoBase
.class);
82 protected int flushAfterNo
= 1000; // large numbers may cause
83 // synchronisation errors when commiting
86 protected Class
<T
> type
;
89 // @Qualifier("defaultBeanInitializer")
90 protected IBeanInitializer defaultBeanInitializer
;
92 public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer
) {
93 this.defaultBeanInitializer
= defaultBeanInitializer
;
97 private ReferringObjectMetadataFactory referringObjectMetadataFactory
;
99 public CdmEntityDaoBase(Class
<T
> type
) {
102 logger
.debug("Creating DAO of type [" + type
.getSimpleName() + "]");
106 public void lock(T t
, LockOptions lockOptions
) {
107 getSession().buildLockRequest(lockOptions
).lock(t
);
111 public void refresh(T t
, LockOptions lockOptions
, List
<String
> propertyPaths
) {
112 getSession().refresh(t
, lockOptions
);
113 defaultBeanInitializer
.initialize(t
, propertyPaths
);
116 // TODO this method should be moved to a concrete class (not typed)
117 public UUID
saveCdmObj(CdmBase cdmObj
) throws DataAccessException
{
118 getSession().saveOrUpdate(cdmObj
);
119 return cdmObj
.getUuid();
122 // TODO: Replace saveCdmObj() by saveCdmObject_
123 private UUID
saveCdmObject_(T cdmObj
) {
124 getSession().saveOrUpdate(cdmObj
);
125 return cdmObj
.getUuid();
128 // TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of
129 // ServiceBase.saveCdmObjectAll()?
130 // TODO: why does this use saveCdmObject_ which actually savesOrUpdateds
133 public Map
<UUID
, T
> saveAll(Collection
<T
> cdmObjCollection
) {
134 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
136 if (logger
.isDebugEnabled()) {
137 logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);
141 Map
<UUID
, T
> resultMap
= new HashMap
<>();
142 Iterator
<T
> iterator
= cdmObjCollection
.iterator();
144 while (iterator
.hasNext()) {
145 if (((i
% 2000) == 0) && (i
> 0)) {
146 logger
.debug("Saved " + i
+ " objects");
148 T cdmObj
= iterator
.next();
149 UUID uuid
= saveCdmObject_(cdmObj
);
150 if (logger
.isDebugEnabled()) {
151 logger
.debug("Save cdmObj: " + (cdmObj
== null ?
null : cdmObj
.toString()));
153 resultMap
.put(uuid
, cdmObj
);
155 if ((i
% flushAfterNo
) == 0) {
157 if (logger
.isDebugEnabled()) {
158 logger
.debug("flush");
161 } catch (Exception e
) {
162 logger
.error("An exception occurred when trying to flush data");
164 throw new RuntimeException(e
);
169 if (logger
.isInfoEnabled()) {
170 logger
.info("Saved " + i
+ " objects");
175 private UUID
saveOrUpdateCdmObject(T cdmObj
) {
176 getSession().saveOrUpdate(cdmObj
);
177 return cdmObj
.getUuid();
181 public Map
<UUID
, T
> saveOrUpdateAll(Collection
<T
> cdmObjCollection
) {
182 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
184 if (logger
.isDebugEnabled()) {
185 logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);
189 Map
<UUID
, T
> resultMap
= new HashMap
<>();
190 Iterator
<T
> iterator
= cdmObjCollection
.iterator();
192 while (iterator
.hasNext()) {
193 if (((i
% 2000) == 0) && (i
> 0)) {
194 logger
.debug("Saved " + i
+ " objects");
196 T cdmObj
= iterator
.next();
197 UUID uuid
= saveOrUpdateCdmObject(cdmObj
);
198 if (logger
.isDebugEnabled()) {
199 logger
.debug("Save cdmObj: " + (cdmObj
== null ?
null : cdmObj
.toString()));
201 resultMap
.put(uuid
, cdmObj
);
203 if ((i
% flushAfterNo
) == 0) {
205 if (logger
.isDebugEnabled()) {
206 logger
.debug("flush");
209 } catch (Exception e
) {
210 logger
.error("An exception occurred when trying to flush data");
212 throw new RuntimeException(e
);
217 if (logger
.isInfoEnabled()) {
218 logger
.info("Saved " + i
+ " objects");
224 public T
replace(T x
, T y
) {
229 Class
<?
> commonClass
= x
.getClass();
231 while (!commonClass
.isAssignableFrom(y
.getClass())) {
232 if (commonClass
.equals(type
)) {
233 throw new RuntimeException();
235 commonClass
= commonClass
.getSuperclass();
239 getSession().merge(x
);
241 Set
<ReferringObjectMetadata
> referringObjectMetas
= referringObjectMetadataFactory
.get(x
.getClass());
243 for (ReferringObjectMetadata referringObjectMetadata
: referringObjectMetas
) {
245 List
<CdmBase
> referringObjects
= referringObjectMetadata
.getReferringObjects(x
, getSession());
247 for (CdmBase referringObject
: referringObjects
) {
249 referringObjectMetadata
.replace(referringObject
, x
, y
);
250 getSession().update(referringObject
);
252 } catch (IllegalArgumentException e
) {
253 throw new RuntimeException(e
.getMessage(), e
);
254 } catch (IllegalAccessException e
) {
255 throw new RuntimeException(e
.getMessage(), e
);
263 public Session
getSession() throws DataAccessException
{
264 return super.getSession();
268 public void clear() throws DataAccessException
{
269 Session session
= getSession();
271 if (logger
.isDebugEnabled()) {
272 logger
.debug("dao clear end");
277 public MergeResult
<T
> merge(T transientObject
, boolean returnTransientEntity
) throws DataAccessException
{
278 Session session
= getSession();
279 PostMergeEntityListener
.addSession(session
);
280 MergeResult
<T
> result
= null;
282 @SuppressWarnings("unchecked")
283 T persistentObject
= (T
) session
.merge(transientObject
);
284 if (logger
.isDebugEnabled()) {
285 logger
.debug("dao merge end");
288 if (returnTransientEntity
) {
289 if (transientObject
!= null && persistentObject
!= null) {
290 transientObject
.setId(persistentObject
.getId());
292 result
= new MergeResult(transientObject
, PostMergeEntityListener
.getNewEntities(session
));
294 result
= new MergeResult(persistentObject
, null);
298 PostMergeEntityListener
.removeSession(session
);
303 public T
merge(T transientObject
) throws DataAccessException
{
304 Session session
= getSession();
305 @SuppressWarnings("unchecked")
306 T persistentObject
= (T
) session
.merge(transientObject
);
307 if (logger
.isDebugEnabled()) {
308 logger
.debug("dao merge end");
310 return persistentObject
;
314 public UUID
saveOrUpdate(T transientObject
) throws DataAccessException
{
315 if (transientObject
== null) {
316 logger
.warn("Object to save should not be null. NOP");
320 if (logger
.isDebugEnabled()) {
321 logger
.debug("dao saveOrUpdate start...");
323 if (logger
.isDebugEnabled()) {
324 logger
.debug("transientObject(" + transientObject
.getClass().getSimpleName() + ") ID:"
325 + transientObject
.getId() + ", UUID: " + transientObject
.getUuid());
327 Session session
= getSession();
328 if (transientObject
.getId() != 0 && VersionableEntity
.class.isAssignableFrom(transientObject
.getClass())) {
329 VersionableEntity versionableEntity
= (VersionableEntity
) transientObject
;
330 versionableEntity
.setUpdated(new DateTime());
331 Authentication authentication
= SecurityContextHolder
.getContext().getAuthentication();
332 if (authentication
!= null && authentication
.getPrincipal() != null
333 && authentication
.getPrincipal() instanceof User
) {
334 User user
= (User
) authentication
.getPrincipal();
335 versionableEntity
.setUpdatedBy(user
);
338 session
.saveOrUpdate(transientObject
);
339 if (logger
.isDebugEnabled()) {
340 logger
.debug("dao saveOrUpdate end");
342 return transientObject
.getUuid();
343 } catch (NonUniqueObjectException e
) {
344 logger
.error("Error in CdmEntityDaoBase.saveOrUpdate(obj). ID=" + e
.getIdentifier() + ". Class="
345 + e
.getEntityName());
346 logger
.error(e
.getMessage());
350 } catch (HibernateException e
) {
358 public T
save(T newInstance
) throws DataAccessException
{
359 if (newInstance
== null) {
360 logger
.warn("Object to save should not be null. NOP");
363 getSession().save(newInstance
);
368 public UUID
update(T transientObject
) throws DataAccessException
{
369 if (transientObject
== null) {
370 logger
.warn("Object to update should not be null. NOP");
373 getSession().update(transientObject
);
374 return transientObject
.getUuid();
378 public UUID
refresh(T persistentObject
) throws DataAccessException
{
379 getSession().refresh(persistentObject
);
380 return persistentObject
.getUuid();
384 public UUID
delete(T persistentObject
) throws DataAccessException
{
385 if (persistentObject
== null) {
386 logger
.warn(type
.getName() + " was 'null'");
390 // Merge the object in if it is detached
392 // I think this is preferable to catching lazy initialization errors
393 // as that solution only swallows and hides the exception, but doesn't
394 // actually solve it.
395 persistentObject
= (T
) getSession().merge(persistentObject
);
396 getSession().delete(persistentObject
);
397 return persistentObject
.getUuid();
401 public T
findById(int id
) throws DataAccessException
{
402 return getSession().get(type
, id
);
406 public T
findByUuid(UUID uuid
) throws DataAccessException
{
407 return this.findByUuid(uuid
, INCLUDE_UNPUBLISHED
);
410 protected T
findByUuid(UUID uuid
, boolean includeUnpublished
) throws DataAccessException
{
411 Session session
= getSession();
412 Criteria crit
= session
.createCriteria(type
);
413 crit
.add(Restrictions
.eq("uuid", uuid
));
414 crit
.addOrder(Order
.desc("created"));
415 if (IPublishable
.class.isAssignableFrom(type
) && !includeUnpublished
) {
416 crit
.add(Restrictions
.eq("publish", Boolean
.TRUE
));
419 @SuppressWarnings("unchecked")
420 List
<T
> results
= crit
.list();
421 Set
<T
> resultSet
= new HashSet
<>();
422 resultSet
.addAll(results
);
423 if (resultSet
.isEmpty()) {
426 if (resultSet
.size() > 1) {
427 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
429 return results
.get(0);
434 public T
findByUuidWithoutFlush(UUID uuid
) throws DataAccessException
{
435 Session session
= getSession();
436 FlushMode currentFlushMode
= session
.getFlushMode();
438 // set flush mode to manual so that the session does not flush
439 // when before performing the query
440 session
.setFlushMode(FlushMode
.MANUAL
);
441 Criteria crit
= session
.createCriteria(type
);
442 crit
.add(Restrictions
.eq("uuid", uuid
));
443 crit
.addOrder(Order
.desc("created"));
444 @SuppressWarnings("unchecked")
445 List
<T
> results
= crit
.list();
446 if (results
.isEmpty()) {
449 if (results
.size() > 1) {
450 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
452 return results
.get(0);
455 // set back the session flush mode
456 if (currentFlushMode
!= null) {
457 session
.setFlushMode(currentFlushMode
);
463 public List
<T
> loadList(Collection
<Integer
> ids
, List
<String
> propertyPaths
) throws DataAccessException
{
466 return new ArrayList
<T
>(0);
469 Criteria criteria
= prepareList(ids
, null, null, null, "id");
471 if (logger
.isDebugEnabled()) {
472 logger
.debug(criteria
.toString());
475 @SuppressWarnings("unchecked")
476 List
<T
> result
= criteria
.list();
477 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
482 public List
<T
> list(Collection
<UUID
> uuids
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
483 List
<String
> propertyPaths
) throws DataAccessException
{
485 if (uuids
== null || uuids
.isEmpty()) {
486 return new ArrayList
<>();
489 Criteria criteria
= prepareList(uuids
, pageSize
, pageNumber
, orderHints
, "uuid");
490 @SuppressWarnings("unchecked")
491 List
<T
> result
= criteria
.list();
492 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
500 public List
<T
> list(Class
<?
extends T
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
, Integer start
,
501 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
503 Criteria criteria
= createCriteria(type
, restrictions
, false);
505 addLimitAndStart(criteria
, limit
, start
);
506 addOrder(criteria
, orderHints
);
508 @SuppressWarnings("unchecked")
509 List
<T
> result
= criteria
.list();
510 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
515 * @param restrictions
518 private void addRestrictions(List
<Restriction
<?
>> restrictions
, DetachedCriteria criteria
) {
520 if(restrictions
== null || restrictions
.isEmpty()){
524 List
<CriterionWithOperator
> perProperty
= new ArrayList
<>(restrictions
.size());
525 Map
<String
, String
> aliases
= new HashMap
<>();
527 for(Restriction
<?
> restriction
: restrictions
){
528 Collection
<?
extends Object
> values
= restriction
.getValues();
529 JoinType jointype
= restriction
.isNot() ? JoinType
.LEFT_OUTER_JOIN
: JoinType
.INNER_JOIN
;
530 if(values
!= null && !values
.isEmpty()){
532 String propertyPath
= restriction
.getPropertyName();
533 String
[] props
= propertyPath
.split("\\.");
535 if(props
.length
== 1){
536 // direct property of the base type of the criteria
537 propertyName
= propertyPath
;
539 // create aliases if the propertyName is a dot separated property path
540 String aĺiasKey
= jointype
.name() + "_";
541 String aliasedProperty
= null;
543 for(int p
= 0; p
< props
.length
-1; p
++){
544 aĺiasKey
= aĺiasKey
+ (aĺiasKey
.isEmpty() ?
"" : ".") + props
[p
];
545 aliasedProperty
= alias
+ (alias
.isEmpty() ?
"" : ".") + props
[p
];
546 if(!aliases
.containsKey(aliasedProperty
)){
547 alias
= alias
+ (alias
.isEmpty() ?
"" : "_" ) + props
[p
];
548 aliases
.put(aĺiasKey
, alias
);
549 criteria
.createAlias(aliasedProperty
, alias
, jointype
);
550 if(logger
.isDebugEnabled()){
551 logger
.debug("addRestrictions() alias created with aliasKey " + aĺiasKey
+ " => " + aliasedProperty
+ " as " + alias
);
555 propertyName
= alias
+ "." + props
[props
.length
-1];
558 Criterion
[] predicates
= new Criterion
[values
.size()];
560 for(Object v
: values
){
561 Criterion criterion
= createRestriction(propertyName
, v
, restriction
.getMatchMode());
562 if(restriction
.isNot()){
563 if(props
.length
> 1){
564 criterion
= Restrictions
.or(Restrictions
.not(criterion
), Restrictions
.isNull(propertyName
));
566 criterion
= Restrictions
.not(criterion
);
569 predicates
[i
++] = criterion
;
570 if(logger
.isDebugEnabled()){
571 logger
.debug("addRestrictions() predicate with " + propertyName
+ " " + (restriction
.getMatchMode() == null ?
"=" : restriction
.getMatchMode().name()) + " " + v
.toString());
574 perProperty
.add(new CriterionWithOperator(restriction
.getOperator(), Restrictions
.or(predicates
)));
575 } // check has values
576 } // loop over restrictions
578 Restriction
.Operator firstOperator
= null;
579 if(!perProperty
.isEmpty()){
580 LogicalExpression logicalExpression
= null;
581 for(CriterionWithOperator cwo
: perProperty
){
582 if(logicalExpression
== null){
583 firstOperator
= cwo
.operator
;
584 logicalExpression
= Restrictions
.and(Restrictions
.sqlRestriction("1=1"), cwo
.criterion
);
586 switch(cwo
.operator
){
588 logicalExpression
= Restrictions
.and(logicalExpression
, cwo
.criterion
);
591 logicalExpression
= Restrictions
.or(logicalExpression
, cwo
.criterion
);
594 throw new RuntimeException("Unsupported Operator");
601 criteria
.add(logicalExpression
);
602 // if(firstOperator == Operator.OR){
606 // criteria.add(Restrictions.and(queryStringCriterion, logicalExpression));
609 if(logger
.isDebugEnabled()){
610 logger
.debug("addRestrictions() final criteria: " + criteria
.toString());
615 * @param propertyName
618 * is only applied if the <code>value</code> is a
619 * <code>String</code> object
623 private Criterion
createRestriction(String propertyName
, Object value
, MatchMode matchMode
) {
625 Criterion restriction
;
627 if (logger
.isDebugEnabled()) {
628 logger
.debug("createRestriction() " + propertyName
+ " is null ");
630 restriction
= Restrictions
.isNull(propertyName
);
631 } else if (matchMode
== null || !(value
instanceof String
)) {
632 if (logger
.isDebugEnabled()) {
633 logger
.debug("createRestriction() " + propertyName
+ " = " + value
.toString());
635 restriction
= Restrictions
.eq(propertyName
, value
);
637 String queryString
= (String
) value
;
638 if (logger
.isDebugEnabled()) {
639 logger
.debug("createRestriction() " + propertyName
+ " " + matchMode
.getMatchOperator() + " "
640 + matchMode
.queryStringFrom(queryString
));
642 if (matchMode
== MatchMode
.BEGINNING
) {
643 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.START
);
644 } else if (matchMode
== MatchMode
.END
) {
645 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.END
);
646 } else if (matchMode
== MatchMode
.EXACT
) {
647 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.EXACT
);
649 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
);
659 public long count(Class
<?
extends T
> type
, List
<Restriction
<?
>> restrictions
) {
661 Criteria criteria
= createCriteria(type
, restrictions
, false);
663 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
665 return (Long
) criteria
.uniqueResult();
674 * @param propertyName
677 private Criteria
prepareList(Collection
<?
> uuids
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
678 String propertyName
) {
679 Criteria criteria
= getSession().createCriteria(type
);
680 criteria
.add(Restrictions
.in(propertyName
, uuids
));
682 if (pageSize
!= null) {
683 criteria
.setMaxResults(pageSize
);
684 if (pageNumber
!= null) {
685 criteria
.setFirstResult(pageNumber
* pageSize
);
687 criteria
.setFirstResult(0);
691 if (orderHints
== null) {
692 orderHints
= OrderHint
.defaultOrderHintsFor(type
);
694 addOrder(criteria
, orderHints
);
702 private Criteria
criterionForType(Class
<?
extends T
> clazz
) {
703 return getSession().createCriteria(entityType(clazz
));
706 protected Class
<?
extends T
> entityType(Class
<?
extends T
> clazz
){
715 public T
load(UUID uuid
) {
716 T bean
= findByUuid(uuid
);
720 defaultBeanInitializer
.load(bean
);
726 public T
load(int id
, List
<String
> propertyPaths
) {
727 T bean
= findById(id
);
731 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
737 public T
load(UUID uuid
, List
<String
> propertyPaths
) {
738 return this.load(uuid
, INCLUDE_UNPUBLISHED
, propertyPaths
);
741 protected T
load(UUID uuid
, boolean includeUnpublished
, List
<String
> propertyPaths
) {
742 T bean
= findByUuid(uuid
, includeUnpublished
);
746 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
752 public Boolean
exists(UUID uuid
) {
753 if (findByUuid(uuid
) == null) {
760 public long count() {
765 public long count(Class
<?
extends T
> clazz
) {
766 Session session
= getSession();
767 Criteria criteria
= null;
769 criteria
= session
.createCriteria(type
);
771 criteria
= session
.createCriteria(clazz
);
773 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
775 // since hibernate 4 (or so) uniqueResult returns Long, not Integer,
777 // to be casted. Think about returning long rather then int!
778 return (long) criteria
.uniqueResult();
782 public List
<T
> list(Integer limit
, Integer start
) {
783 return list(limit
, start
, null);
787 public List
<Object
[]> group(Class
<?
extends T
> clazz
, Integer limit
, Integer start
, List
<Grouping
> groups
,
788 List
<String
> propertyPaths
) {
790 Criteria criteria
= null;
791 criteria
= criterionForType(clazz
);
793 addGroups(criteria
, groups
);
796 criteria
.setFirstResult(start
);
797 criteria
.setMaxResults(limit
);
800 @SuppressWarnings("unchecked")
801 List
<Object
[]> result
= criteria
.list();
803 if (propertyPaths
!= null && !propertyPaths
.isEmpty()) {
804 for (Object
[] objects
: result
) {
805 defaultBeanInitializer
.initialize(objects
[0], propertyPaths
);
812 protected void countGroups(DetachedCriteria criteria
, List
<Grouping
> groups
) {
813 if (groups
!= null) {
815 Map
<String
, String
> aliases
= new HashMap
<String
, String
>();
817 for (Grouping grouping
: groups
) {
818 if (grouping
.getAssociatedObj() != null) {
820 if ((alias
= aliases
.get(grouping
.getAssociatedObj())) == null) {
821 alias
= grouping
.getAssociatedObjectAlias();
822 aliases
.put(grouping
.getAssociatedObj(), alias
);
823 criteria
.createAlias(grouping
.getAssociatedObj(), alias
);
828 ProjectionList projectionList
= Projections
.projectionList();
830 for (Grouping grouping
: groups
) {
831 grouping
.addProjection(projectionList
);
833 criteria
.setProjection(projectionList
);
837 protected void addGroups(Criteria criteria
, List
<Grouping
> groups
) {
838 if (groups
!= null) {
840 Map
<String
, String
> aliases
= new HashMap
<String
, String
>();
842 for (Grouping grouping
: groups
) {
843 if (grouping
.getAssociatedObj() != null) {
845 if ((alias
= aliases
.get(grouping
.getAssociatedObj())) == null) {
846 alias
= grouping
.getAssociatedObjectAlias();
847 aliases
.put(grouping
.getAssociatedObj(), alias
);
848 criteria
.createAlias(grouping
.getAssociatedObj(), alias
);
853 ProjectionList projectionList
= Projections
.projectionList();
855 for (Grouping grouping
: groups
) {
856 grouping
.addProjection(projectionList
);
858 criteria
.setProjection(projectionList
);
860 for (Grouping grouping
: groups
) {
861 grouping
.addOrder(criteria
);
868 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
869 return list(limit
, start
, orderHints
, null);
873 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
874 Criteria criteria
= getSession().createCriteria(type
);
876 criteria
.setFirstResult(start
);
877 criteria
.setMaxResults(limit
);
880 addOrder(criteria
, orderHints
);
881 @SuppressWarnings("unchecked")
882 List
<T
> results
= criteria
.list();
884 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
889 public <S
extends T
> List
<S
> list(Class
<S
> clazz
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
,
890 List
<String
> propertyPaths
) {
891 Criteria criteria
= null;
893 criteria
= getSession().createCriteria(type
);
895 criteria
= getSession().createCriteria(clazz
);
898 addLimitAndStart(criteria
, limit
, start
);
900 addOrder(criteria
, orderHints
);
902 @SuppressWarnings("unchecked")
903 List
<S
> results
= criteria
.list();
905 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
909 public <S
extends T
> List
<S
> list(Class
<S
> type
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
910 return list(type
, limit
, start
, orderHints
, null);
914 public <S
extends T
> List
<S
> list(Class
<S
> type
, Integer limit
, Integer start
) {
915 return list(type
, limit
, start
, null, null);
919 public Class
<T
> getType() {
923 protected void setPagingParameter(Query query
, Integer pageSize
, Integer pageIndex
) {
924 if (pageSize
!= null) {
925 query
.setMaxResults(pageSize
);
926 if (pageIndex
!= null) {
927 query
.setFirstResult(pageIndex
* pageSize
);
929 query
.setFirstResult(0);
934 protected void setPagingParameter(AuditQuery query
, Integer pageSize
, Integer pageIndex
) {
935 if (pageSize
!= null) {
936 query
.setMaxResults(pageSize
);
937 if (pageIndex
!= null) {
938 query
.setFirstResult(pageIndex
* pageSize
);
940 query
.setFirstResult(0);
946 public long count(T example
, Set
<String
> includeProperties
) {
947 Criteria criteria
= getSession().createCriteria(example
.getClass());
948 addExample(criteria
, example
, includeProperties
);
950 criteria
.setProjection(Projections
.rowCount());
951 return (Long
) criteria
.uniqueResult();
954 protected void addExample(Criteria criteria
, T example
, Set
<String
> includeProperties
) {
955 if (includeProperties
!= null && !includeProperties
.isEmpty()) {
956 criteria
.add(Example
.create(example
).setPropertySelector(new PropertySelectorImpl(includeProperties
)));
957 ClassMetadata classMetadata
= getSession().getSessionFactory().getClassMetadata(example
.getClass());
958 for (String property
: includeProperties
) {
959 Type type
= classMetadata
.getPropertyType(property
);
960 if (type
.isEntityType()) {
962 Field field
= ReflectionUtils
.findField(example
.getClass(), property
);
963 field
.setAccessible(true);
964 Object value
= field
.get(example
);
966 criteria
.add(Restrictions
.eq(property
, value
));
968 criteria
.add(Restrictions
.isNull(property
));
970 } catch (SecurityException se
) {
971 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
973 } catch (HibernateException he
) {
974 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
976 } catch (IllegalArgumentException iae
) {
977 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
979 } catch (IllegalAccessException ie
) {
980 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
987 criteria
.add(Example
.create(example
));
993 * NOTE: We can't reuse
994 * {@link #list(Class, String, Object, MatchMode, Integer, Integer, List, List)
995 * here due to different default behavior of the <code>matchmode</code>
1000 * @param queryString
1006 * @param propertyPaths
1010 public List
<T
> findByParam(Class
<?
extends T
> clazz
, String param
, String queryString
, MatchMode matchmode
,
1011 List
<Criterion
> criterion
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
1012 List
<String
> propertyPaths
) {
1014 Criteria criteria
= criterionForType(clazz
);
1016 if (queryString
!= null) {
1017 if (matchmode
== null) {
1018 criteria
.add(Restrictions
.ilike(param
, queryString
));
1019 } else if (matchmode
== MatchMode
.BEGINNING
) {
1020 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.START
));
1021 } else if (matchmode
== MatchMode
.END
) {
1022 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.END
));
1023 } else if (matchmode
== MatchMode
.EXACT
) {
1024 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.EXACT
));
1026 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
));
1030 addCriteria(criteria
, criterion
);
1032 if (pageSize
!= null) {
1033 criteria
.setMaxResults(pageSize
);
1034 if (pageNumber
!= null) {
1035 criteria
.setFirstResult(pageNumber
* pageSize
);
1037 criteria
.setFirstResult(0);
1041 addOrder(criteria
, orderHints
);
1043 @SuppressWarnings("unchecked")
1044 List
<T
> result
= criteria
.list();
1045 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
1053 * @param queryString
1059 public long countByParam(Class
<?
extends T
> clazz
, String param
, String queryString
, MatchMode matchmode
,
1060 List
<Criterion
> criterion
) {
1062 Criteria criteria
= null;
1064 criteria
= criterionForType(clazz
);
1066 if (queryString
!= null) {
1067 if (matchmode
== null) {
1068 criteria
.add(Restrictions
.ilike(param
, queryString
));
1069 } else if (matchmode
== MatchMode
.BEGINNING
) {
1070 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.START
));
1071 } else if (matchmode
== MatchMode
.END
) {
1072 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.END
));
1073 } else if (matchmode
== MatchMode
.EXACT
) {
1074 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.EXACT
));
1076 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
));
1080 addCriteria(criteria
, criterion
);
1082 criteria
.setProjection(Projections
.rowCount());
1084 return (Long
) criteria
.uniqueResult();
1088 * Creates a criteria query for the CDM <code>type</code> either for counting or listing matching entities.
1090 * The set of matching entities can be restricted by passing a list of {@link Restriction} objects.
1091 * Restrictions can logically combined:
1094 new Restriction<String>("titleCache", MatchMode.ANYWHERE, "foo"),
1095 new Restriction<String>("institute.name", Operator.OR, MatchMode.BEGINNING, "Bar")
1098 * The first Restriction in the example above by default has the <code>Operator.AND</code> which will be
1099 * ignored since this is the first restriction. The <code>Operator</code> of further restrictions in the
1100 * list are used to combine with the previous restriction.
1103 * @param restrictions
1107 protected Criteria
createCriteria(Class
<?
extends T
> type
, List
<Restriction
<?
>> restrictions
, boolean doCount
) {
1109 DetachedCriteria idsOnlyCriteria
= DetachedCriteria
.forClass(entityType(type
));
1110 idsOnlyCriteria
.setProjection(Projections
.distinct(Projections
.id()));
1112 addRestrictions(restrictions
, idsOnlyCriteria
);
1114 Criteria criteria
= criterionForType(type
);
1115 criteria
.add(Subqueries
.propertyIn("id", idsOnlyCriteria
));
1118 criteria
.setProjection(Projections
.rowCount());
1120 idsOnlyCriteria
.setProjection(Projections
.distinct(Projections
.property("id")));
1128 public List
<T
> findByParamWithRestrictions(Class
<?
extends T
> clazz
, String param
, String queryString
,
1129 MatchMode matchmode
, List
<Restriction
<?
>> restrictions
, Integer pageSize
, Integer pageNumber
,
1130 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1132 List
<Restriction
<?
>> allRestrictions
= new ArrayList
<>();
1133 allRestrictions
.add(new Restriction
<String
>(param
, matchmode
, queryString
));
1134 if(restrictions
!= null){
1135 allRestrictions
.addAll(restrictions
);
1137 Criteria criteria
= createCriteria(clazz
, allRestrictions
, false);
1139 if (pageSize
!= null) {
1140 criteria
.setMaxResults(pageSize
);
1141 if (pageNumber
!= null) {
1142 criteria
.setFirstResult(pageNumber
* pageSize
);
1144 criteria
.setFirstResult(0);
1148 addOrder(criteria
, orderHints
);
1150 @SuppressWarnings("unchecked")
1151 List
<T
> result
= criteria
.list();
1152 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
1158 public long countByParamWithRestrictions(Class
<?
extends T
> clazz
, String param
, String queryString
,
1159 MatchMode matchmode
, List
<Restriction
<?
>> restrictions
) {
1161 List
<Restriction
<?
>> allRestrictions
= new ArrayList
<>();
1162 allRestrictions
.add(new Restriction
<String
>(param
, matchmode
, queryString
));
1163 if(restrictions
!= null){
1164 allRestrictions
.addAll(restrictions
);
1166 Criteria criteria
= createCriteria(clazz
, allRestrictions
, true);
1168 return (Long
) criteria
.uniqueResult();
1172 public List
<T
> list(T example
, Set
<String
> includeProperties
, Integer limit
, Integer start
,
1173 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1174 Criteria criteria
= getSession().createCriteria(example
.getClass());
1175 addExample(criteria
, example
, includeProperties
);
1177 addLimitAndStart(criteria
, limit
, start
);
1179 addOrder(criteria
, orderHints
);
1181 @SuppressWarnings("unchecked")
1182 List
<T
> results
= criteria
.list();
1183 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
1187 private class PropertySelectorImpl
implements PropertySelector
{
1189 private final Set
<String
> includeProperties
;
1193 private static final long serialVersionUID
= -3175311800911570546L;
1195 public PropertySelectorImpl(Set
<String
> includeProperties
) {
1196 this.includeProperties
= includeProperties
;
1200 public boolean include(Object propertyValue
, String propertyName
, Type type
) {
1201 if (includeProperties
.contains(propertyName
)) {
1210 private class CriterionWithOperator
{
1212 Restriction
.Operator operator
;
1213 Criterion criterion
;
1216 public CriterionWithOperator(Operator operator
, Criterion criterion
) {
1218 this.operator
= operator
;
1219 this.criterion
= criterion
;
1226 * Returns a Criteria for the given {@link Class class} or, if
1227 * <code>null</code>, for the base {@link Class class} of this DAO.
1230 * @return the Criteria
1232 protected Criteria
getCriteria(Class
<?
extends CdmBase
> clazz
) {
1233 Criteria criteria
= null;
1234 if (clazz
== null) {
1235 criteria
= getSession().createCriteria(type
);
1237 criteria
= getSession().createCriteria(clazz
);
1247 protected AuditQuery
makeAuditQuery(Class
<?
extends CdmBase
> clazz
, AuditEvent auditEvent
) {
1248 AuditQuery query
= null;
1250 if (clazz
== null) {
1251 query
= getAuditReader().createQuery().forEntitiesAtRevision(type
, auditEvent
.getRevisionNumber());
1253 query
= getAuditReader().createQuery().forEntitiesAtRevision(clazz
, auditEvent
.getRevisionNumber());
1258 protected AuditReader
getAuditReader() {
1259 return AuditReaderFactory
.get(getSession());