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
.EnumSet
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.Iterator
;
19 import java
.util
.List
;
22 import java
.util
.UUID
;
24 import org
.apache
.log4j
.Logger
;
25 import org
.hibernate
.Criteria
;
26 import org
.hibernate
.FlushMode
;
27 import org
.hibernate
.HibernateException
;
28 import org
.hibernate
.LockOptions
;
29 import org
.hibernate
.NonUniqueObjectException
;
30 import org
.hibernate
.Query
;
31 import org
.hibernate
.Session
;
32 import org
.hibernate
.criterion
.Criterion
;
33 import org
.hibernate
.criterion
.DetachedCriteria
;
34 import org
.hibernate
.criterion
.Disjunction
;
35 import org
.hibernate
.criterion
.Example
;
36 import org
.hibernate
.criterion
.Example
.PropertySelector
;
37 import org
.hibernate
.criterion
.LogicalExpression
;
38 import org
.hibernate
.criterion
.Order
;
39 import org
.hibernate
.criterion
.ProjectionList
;
40 import org
.hibernate
.criterion
.Projections
;
41 import org
.hibernate
.criterion
.Restrictions
;
42 import org
.hibernate
.criterion
.Subqueries
;
43 import org
.hibernate
.envers
.AuditReader
;
44 import org
.hibernate
.envers
.AuditReaderFactory
;
45 import org
.hibernate
.envers
.query
.AuditQuery
;
46 import org
.hibernate
.metadata
.ClassMetadata
;
47 import org
.hibernate
.sql
.JoinType
;
48 import org
.hibernate
.type
.Type
;
49 import org
.joda
.time
.DateTime
;
50 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
51 import org
.springframework
.dao
.DataAccessException
;
52 import org
.springframework
.dao
.InvalidDataAccessApiUsageException
;
53 import org
.springframework
.security
.core
.Authentication
;
54 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
55 import org
.springframework
.stereotype
.Repository
;
56 import org
.springframework
.util
.ReflectionUtils
;
58 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
59 import eu
.etaxonomy
.cdm
.model
.common
.IPublishable
;
60 import eu
.etaxonomy
.cdm
.model
.common
.VersionableEntity
;
61 import eu
.etaxonomy
.cdm
.model
.permission
.User
;
62 import eu
.etaxonomy
.cdm
.model
.view
.AuditEvent
;
63 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmEntityDao
;
64 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
65 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
.Operator
;
66 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
67 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
68 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.PostMergeEntityListener
;
69 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.replace
.ReferringObjectMetadata
;
70 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.replace
.ReferringObjectMetadataFactory
;
71 import eu
.etaxonomy
.cdm
.persistence
.query
.Grouping
;
72 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
73 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
76 * @author a.mueller FIXME CdmEntityDaoBase is abstract, can it be annotated
80 public abstract class CdmEntityDaoBase
<T
extends CdmBase
>
82 implements ICdmEntityDao
<T
> {
84 private static final Logger logger
= Logger
.getLogger(CdmEntityDaoBase
.class);
86 protected int flushAfterNo
= 1000; // large numbers may cause
87 // synchronisation errors when commiting
90 protected Class
<T
> type
;
93 // @Qualifier("defaultBeanInitializer")
94 protected IBeanInitializer defaultBeanInitializer
;
96 public void setDefaultBeanInitializer(IBeanInitializer defaultBeanInitializer
) {
97 this.defaultBeanInitializer
= defaultBeanInitializer
;
101 private ReferringObjectMetadataFactory referringObjectMetadataFactory
;
103 protected static final EnumSet
<Operator
> LEFTOUTER_OPS
= EnumSet
.of(Operator
.AND_NOT
, Operator
.OR
, Operator
.OR_NOT
);
105 public CdmEntityDaoBase(Class
<T
> type
) {
108 logger
.debug("Creating DAO of type [" + type
.getSimpleName() + "]");
112 public void lock(T t
, LockOptions lockOptions
) {
113 getSession().buildLockRequest(lockOptions
).lock(t
);
117 public void refresh(T t
, LockOptions lockOptions
, List
<String
> propertyPaths
) {
118 getSession().refresh(t
, lockOptions
);
119 defaultBeanInitializer
.initialize(t
, propertyPaths
);
122 // TODO this method should be moved to a concrete class (not typed)
123 public UUID
saveCdmObj(CdmBase cdmObj
) throws DataAccessException
{
124 getSession().saveOrUpdate(cdmObj
);
125 return cdmObj
.getUuid();
128 // TODO: Replace saveCdmObj() by saveCdmObject_
129 private UUID
saveCdmObject_(T cdmObj
) {
130 getSession().saveOrUpdate(cdmObj
);
131 return cdmObj
.getUuid();
134 // TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of
135 // ServiceBase.saveCdmObjectAll()?
136 // TODO: why does this use saveCdmObject_ which actually savesOrUpdateds
139 public Map
<UUID
, T
> saveAll(Collection
<?
extends T
> cdmObjCollection
) {
140 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
142 if (logger
.isDebugEnabled()) {
143 logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);
147 Map
<UUID
, T
> resultMap
= new HashMap
<>();
148 Iterator
<?
extends T
> iterator
= cdmObjCollection
.iterator();
150 while (iterator
.hasNext()) {
151 if (((i
% 2000) == 0) && (i
> 0)) {
152 logger
.debug("Saved " + i
+ " objects");
154 T cdmObj
= iterator
.next();
155 UUID uuid
= saveCdmObject_(cdmObj
);
156 if (logger
.isDebugEnabled()) {
157 logger
.debug("Save cdmObj: " + (cdmObj
== null ?
null : cdmObj
.toString()));
159 resultMap
.put(uuid
, cdmObj
);
161 if ((i
% flushAfterNo
) == 0) {
163 if (logger
.isDebugEnabled()) {
164 logger
.debug("flush");
167 } catch (Exception e
) {
168 logger
.error("An exception occurred when trying to flush data");
170 throw new RuntimeException(e
);
175 if (logger
.isInfoEnabled()) {
176 logger
.info("Saved " + i
+ " objects");
181 private UUID
saveOrUpdateCdmObject(T cdmObj
) {
182 getSession().saveOrUpdate(cdmObj
);
183 return cdmObj
.getUuid();
187 public Map
<UUID
, T
> saveOrUpdateAll(Collection
<T
> cdmObjCollection
) {
188 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
190 if (logger
.isDebugEnabled()) {
191 logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);
195 Map
<UUID
, T
> resultMap
= new HashMap
<>();
196 Iterator
<T
> iterator
= cdmObjCollection
.iterator();
198 while (iterator
.hasNext()) {
199 if (((i
% 2000) == 0) && (i
> 0)) {
200 logger
.debug("Saved " + i
+ " objects");
202 T cdmObj
= iterator
.next();
203 UUID uuid
= saveOrUpdateCdmObject(cdmObj
);
204 if (logger
.isDebugEnabled()) {
205 logger
.debug("Save cdmObj: " + (cdmObj
== null ?
null : cdmObj
.toString()));
207 resultMap
.put(uuid
, cdmObj
);
209 if ((i
% flushAfterNo
) == 0) {
211 if (logger
.isDebugEnabled()) {
212 logger
.debug("flush");
215 } catch (Exception e
) {
216 logger
.error("An exception occurred when trying to flush data");
218 throw new RuntimeException(e
);
223 if (logger
.isInfoEnabled()) {
224 logger
.info("Saved " + i
+ " objects");
230 public T
replace(T x
, T y
) {
235 Class
<?
> commonClass
= x
.getClass();
237 while (!commonClass
.isAssignableFrom(y
.getClass())) {
238 if (commonClass
.equals(type
)) {
239 throw new RuntimeException();
241 commonClass
= commonClass
.getSuperclass();
245 getSession().merge(x
);
247 Set
<ReferringObjectMetadata
> referringObjectMetas
= referringObjectMetadataFactory
.get(x
.getClass());
249 for (ReferringObjectMetadata referringObjectMetadata
: referringObjectMetas
) {
251 List
<CdmBase
> referringObjects
= referringObjectMetadata
.getReferringObjects(x
, getSession());
253 for (CdmBase referringObject
: referringObjects
) {
255 referringObjectMetadata
.replace(referringObject
, x
, y
);
256 getSession().update(referringObject
);
258 } catch (IllegalArgumentException e
) {
259 throw new RuntimeException(e
.getMessage(), e
);
260 } catch (IllegalAccessException e
) {
261 throw new RuntimeException(e
.getMessage(), e
);
269 public Session
getSession() throws DataAccessException
{
270 return super.getSession();
274 public void clear() throws DataAccessException
{
275 Session session
= getSession();
277 if (logger
.isDebugEnabled()) {
278 logger
.debug("dao clear end");
283 public MergeResult
<T
> merge(T transientObject
, boolean returnTransientEntity
) throws DataAccessException
{
284 Session session
= getSession();
285 PostMergeEntityListener
.addSession(session
);
286 MergeResult
<T
> result
= null;
288 @SuppressWarnings("unchecked")
289 T persistentObject
= (T
) session
.merge(transientObject
);
290 if (logger
.isDebugEnabled()) {
291 logger
.debug("dao merge end");
294 if (returnTransientEntity
) {
295 if (transientObject
!= null && persistentObject
!= null) {
296 transientObject
.setId(persistentObject
.getId());
298 result
= new MergeResult(transientObject
, PostMergeEntityListener
.getNewEntities(session
));
300 result
= new MergeResult(persistentObject
, null);
304 PostMergeEntityListener
.removeSession(session
);
309 public T
merge(T transientObject
) throws DataAccessException
{
310 Session session
= getSession();
311 @SuppressWarnings("unchecked")
312 T persistentObject
= (T
) session
.merge(transientObject
);
313 if (logger
.isDebugEnabled()) {
314 logger
.debug("dao merge end");
316 return persistentObject
;
320 public UUID
saveOrUpdate(T transientObject
) throws DataAccessException
{
321 if (transientObject
== null) {
322 logger
.warn("Object to save should not be null. NOP");
326 if (logger
.isDebugEnabled()) {
327 logger
.debug("dao saveOrUpdate start...");
329 if (logger
.isDebugEnabled()) {
330 logger
.debug("transientObject(" + transientObject
.getClass().getSimpleName() + ") ID:"
331 + transientObject
.getId() + ", UUID: " + transientObject
.getUuid());
333 Session session
= getSession();
334 if (transientObject
.getId() != 0 && VersionableEntity
.class.isAssignableFrom(transientObject
.getClass())) {
335 VersionableEntity versionableEntity
= (VersionableEntity
) transientObject
;
336 versionableEntity
.setUpdated(new DateTime());
337 Authentication authentication
= SecurityContextHolder
.getContext().getAuthentication();
338 if (authentication
!= null && authentication
.getPrincipal() != null
339 && authentication
.getPrincipal() instanceof User
) {
340 User user
= (User
) authentication
.getPrincipal();
341 versionableEntity
.setUpdatedBy(user
);
344 session
.saveOrUpdate(transientObject
);
345 if (logger
.isDebugEnabled()) {
346 logger
.debug("dao saveOrUpdate end");
348 return transientObject
.getUuid();
349 } catch (NonUniqueObjectException e
) {
350 logger
.error("Error in CdmEntityDaoBase.saveOrUpdate(obj). ID=" + e
.getIdentifier() + ". Class="
351 + e
.getEntityName());
352 logger
.error(e
.getMessage());
356 } catch (HibernateException e
) {
364 public <S
extends T
> S
save(S newInstance
) throws DataAccessException
{
365 if (newInstance
== null) {
366 logger
.warn("Object to save should not be null. NOP");
369 getSession().save(newInstance
);
374 public UUID
update(T transientObject
) throws DataAccessException
{
375 if (transientObject
== null) {
376 logger
.warn("Object to update should not be null. NOP");
379 getSession().update(transientObject
);
380 return transientObject
.getUuid();
384 public UUID
refresh(T persistentObject
) throws DataAccessException
{
385 getSession().refresh(persistentObject
);
386 return persistentObject
.getUuid();
390 public UUID
delete(T persistentObject
) throws DataAccessException
{
391 if (persistentObject
== null) {
392 logger
.warn(type
.getName() + " was 'null'");
396 // Merge the object in if it is detached
398 // I think this is preferable to catching lazy initialization errors
399 // as that solution only swallows and hides the exception, but doesn't
400 // actually solve it.
401 persistentObject
= (T
) getSession().merge(persistentObject
);
402 getSession().delete(persistentObject
);
403 return persistentObject
.getUuid();
407 public T
findById(int id
) throws DataAccessException
{
408 return getSession().get(type
, id
);
412 public T
findByUuid(UUID uuid
) throws DataAccessException
{
413 return this.findByUuid(uuid
, INCLUDE_UNPUBLISHED
);
416 protected T
findByUuid(UUID uuid
, boolean includeUnpublished
) throws DataAccessException
{
417 Session session
= getSession();
418 Criteria crit
= session
.createCriteria(type
);
419 crit
.add(Restrictions
.eq("uuid", uuid
));
420 crit
.addOrder(Order
.desc("created"));
421 if (IPublishable
.class.isAssignableFrom(type
) && !includeUnpublished
) {
422 crit
.add(Restrictions
.eq("publish", Boolean
.TRUE
));
425 @SuppressWarnings("unchecked")
426 List
<T
> results
= crit
.list();
427 Set
<T
> resultSet
= new HashSet
<>();
428 resultSet
.addAll(results
);
429 if (resultSet
.isEmpty()) {
432 if (resultSet
.size() > 1) {
433 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
435 return results
.get(0);
440 public T
findByUuidWithoutFlush(UUID uuid
) throws DataAccessException
{
441 Session session
= getSession();
442 FlushMode currentFlushMode
= session
.getFlushMode();
444 // set flush mode to manual so that the session does not flush
445 // when before performing the query
446 session
.setFlushMode(FlushMode
.MANUAL
);
447 Criteria crit
= session
.createCriteria(type
);
448 crit
.add(Restrictions
.eq("uuid", uuid
));
449 crit
.addOrder(Order
.desc("created"));
450 @SuppressWarnings("unchecked")
451 List
<T
> results
= crit
.list();
452 if (results
.isEmpty()) {
455 if (results
.size() > 1) {
456 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
458 return results
.get(0);
461 // set back the session flush mode
462 if (currentFlushMode
!= null) {
463 session
.setFlushMode(currentFlushMode
);
469 public List
<T
> loadList(Collection
<Integer
> ids
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) throws DataAccessException
{
472 return new ArrayList
<>(0);
475 Criteria criteria
= prepareList(null, ids
, null, null, orderHints
, "id");
477 if (logger
.isDebugEnabled()) {
478 logger
.debug(criteria
.toString());
481 @SuppressWarnings("unchecked")
482 List
<T
> result
= criteria
.list();
483 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
488 public List
<T
> list(Collection
<UUID
> uuids
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
489 List
<String
> propertyPaths
) throws DataAccessException
{
491 if (uuids
== null || uuids
.isEmpty()) {
492 return new ArrayList
<>();
495 Criteria criteria
= prepareList(null, uuids
, pageSize
, pageNumber
, orderHints
, "uuid");
496 @SuppressWarnings("unchecked")
497 List
<T
> result
= criteria
.list();
498 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
503 public <S
extends T
> List
<S
> list(Class
<S
> clazz
, Collection
<UUID
> uuids
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
504 List
<String
> propertyPaths
) throws DataAccessException
{
506 if (uuids
== null || uuids
.isEmpty()) {
507 return new ArrayList
<>();
510 Criteria criteria
= prepareList(clazz
, uuids
, pageSize
, pageNumber
, orderHints
, "uuid");
511 @SuppressWarnings("unchecked")
512 List
<S
> result
= criteria
.list();
513 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
518 public <S
extends T
> List
<S
> list(Class
<S
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
, Integer start
,
519 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
521 Criteria criteria
= createCriteria(type
, restrictions
, false);
523 addLimitAndStart(criteria
, limit
, start
);
524 addOrder(criteria
, orderHints
);
526 @SuppressWarnings("unchecked")
527 List
<S
> result
= criteria
.list();
528 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
533 * @param restrictions
536 private void addRestrictions(List
<Restriction
<?
>> restrictions
, DetachedCriteria criteria
) {
538 if(restrictions
== null || restrictions
.isEmpty()){
542 List
<CriterionWithOperator
> perProperty
= new ArrayList
<>(restrictions
.size());
543 Map
<String
, String
> aliases
= new HashMap
<>();
547 for(Restriction
<?
> restriction
: restrictions
){
548 Collection
<?
extends Object
> values
= restriction
.getValues();
549 JoinType jointype
= LEFTOUTER_OPS
.contains(restriction
.getOperator()) ? JoinType
.LEFT_OUTER_JOIN
: JoinType
.INNER_JOIN
;
550 if(values
!= null && !values
.isEmpty()){
552 String propertyPath
= restriction
.getPropertyName();
553 String
[] props
= propertyPath
.split("\\.");
555 if(props
.length
== 1){
556 // direct property of the base type of the criteria
557 propertyName
= propertyPath
;
559 // create aliases if the propertyName is a dot separated property path
560 String aĺiasKey
= jointype
.name() + "_";
561 String aliasedProperty
= null;
563 for(int p
= 0; p
< props
.length
-1; p
++){
564 aĺiasKey
= aĺiasKey
+ (aĺiasKey
.isEmpty() ?
"" : ".") + props
[p
];
565 aliasedProperty
= alias
+ (alias
.isEmpty() ?
"" : ".") + props
[p
];
566 if(!aliases
.containsKey(aliasedProperty
)){
567 alias
= alias
+ (alias
.isEmpty() ?
"" : "_" ) + props
[p
];
568 aliases
.put(aĺiasKey
, alias
);
569 criteria
.createAlias(aliasedProperty
, alias
, jointype
);
570 if(logger
.isDebugEnabled()){
571 logger
.debug("addRestrictions() alias created with aliasKey " + aĺiasKey
+ " => " + aliasedProperty
+ " as " + alias
);
575 propertyName
= alias
+ "." + props
[props
.length
-1];
578 Criterion
[] predicates
= new Criterion
[values
.size()];
580 for(Object v
: values
){
581 Criterion criterion
= createRestriction(propertyName
, v
, restriction
.getMatchMode());
582 if(restriction
.isNot()){
583 if(props
.length
> 1){
584 criterion
= Restrictions
.or(Restrictions
.not(criterion
), Restrictions
.isNull(propertyName
));
586 criterion
= Restrictions
.not(criterion
);
589 predicates
[i
++] = criterion
;
590 if(logger
.isDebugEnabled()){
591 logger
.debug("addRestrictions() predicate with " + propertyName
+ " " + (restriction
.getMatchMode() == null ?
"=" : restriction
.getMatchMode().name()) + " " + v
.toString());
594 if(restriction
.getOperator() == Operator
.AND_NOT
){
595 perProperty
.add(new CriterionWithOperator(restriction
.getOperator(), Restrictions
.and(predicates
)));
597 perProperty
.add(new CriterionWithOperator(restriction
.getOperator(), Restrictions
.or(predicates
)));
599 } // check has values
600 } // loop over restrictions
602 Restriction
.Operator firstOperator
= null;
603 if(!perProperty
.isEmpty()){
604 LogicalExpression logicalExpression
= null;
605 for(CriterionWithOperator cwo
: perProperty
){
606 if(logicalExpression
== null){
607 firstOperator
= cwo
.operator
;
608 logicalExpression
= Restrictions
.and(Restrictions
.sqlRestriction("1=1"), cwo
.criterion
);
610 switch(cwo
.operator
){
613 logicalExpression
= Restrictions
.and(logicalExpression
, cwo
.criterion
);
617 logicalExpression
= Restrictions
.or(logicalExpression
, cwo
.criterion
);
620 throw new RuntimeException("Unsupported Operator");
627 criteria
.add(logicalExpression
);
628 // if(firstOperator == Operator.OR){
632 // criteria.add(Restrictions.and(queryStringCriterion, logicalExpression));
635 if(logger
.isDebugEnabled()){
636 logger
.debug("addRestrictions() final criteria: " + criteria
.toString());
641 * @param propertyName
644 * is only applied if the <code>value</code> is a
645 * <code>String</code> object
649 private Criterion
createRestriction(String propertyName
, Object value
, MatchMode matchMode
) {
651 Criterion restriction
;
653 if (logger
.isDebugEnabled()) {
654 logger
.debug("createRestriction() " + propertyName
+ " is null ");
656 restriction
= Restrictions
.isNull(propertyName
);
657 } else if (matchMode
== null || !(value
instanceof String
)) {
658 if (logger
.isDebugEnabled()) {
659 logger
.debug("createRestriction() " + propertyName
+ " = " + value
.toString());
661 restriction
= Restrictions
.eq(propertyName
, value
);
663 String queryString
= (String
) value
;
664 if (logger
.isDebugEnabled()) {
665 logger
.debug("createRestriction() " + propertyName
+ " " + matchMode
.getMatchOperator() + " "
666 + matchMode
.queryStringFrom(queryString
));
670 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.START
);
673 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.END
);
676 restriction
= Restrictions
.ilike(propertyName
, matchMode
.queryStringFrom(queryString
), org
.hibernate
.criterion
.MatchMode
.ANYWHERE
);
679 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.EXACT
);
682 restriction
= Restrictions
.ilike(propertyName
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
);
685 throw new RuntimeException("Unknown MatchMode: " + matchMode
.name());
695 public long count(Class
<?
extends T
> type
, List
<Restriction
<?
>> restrictions
) {
697 Criteria criteria
= createCriteria(type
, restrictions
, false);
699 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
701 return (Long
) criteria
.uniqueResult();
710 * @param propertyName
713 private Criteria
prepareList(Class
<?
extends T
> clazz
, Collection
<?
> uuids
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
714 String propertyName
) {
718 Criteria criteria
= getSession().createCriteria(clazz
);
719 criteria
.add(Restrictions
.in(propertyName
, uuids
));
721 if (pageSize
!= null) {
722 criteria
.setMaxResults(pageSize
);
723 if (pageNumber
!= null) {
724 criteria
.setFirstResult(pageNumber
* pageSize
);
726 criteria
.setFirstResult(0);
730 if (orderHints
== null) {
731 orderHints
= OrderHint
.defaultOrderHintsFor(type
);
733 addOrder(criteria
, orderHints
);
741 private Criteria
criterionForType(Class
<?
extends T
> clazz
) {
742 return getSession().createCriteria(entityType(clazz
));
745 protected Class
<?
extends T
> entityType(Class
<?
extends T
> clazz
){
754 public T
load(UUID uuid
) {
755 T bean
= findByUuid(uuid
);
759 defaultBeanInitializer
.load(bean
);
765 public T
load(int id
, List
<String
> propertyPaths
) {
766 T bean
= findById(id
);
770 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
776 public T
loadWithoutInitializing(int id
){
777 return this.getSession().load(type
, id
);
781 public T
load(UUID uuid
, List
<String
> propertyPaths
) {
782 return this.load(uuid
, INCLUDE_UNPUBLISHED
, propertyPaths
);
785 protected T
load(UUID uuid
, boolean includeUnpublished
, List
<String
> propertyPaths
) {
786 T bean
= findByUuid(uuid
, includeUnpublished
);
790 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
796 public Boolean
exists(UUID uuid
) {
797 if (findByUuid(uuid
) == null) {
804 public long count() {
809 public long count(Class
<?
extends T
> clazz
) {
810 Session session
= getSession();
811 Criteria criteria
= null;
813 criteria
= session
.createCriteria(type
);
815 criteria
= session
.createCriteria(clazz
);
817 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
819 // since hibernate 4 (or so) uniqueResult returns Long, not Integer,
821 // to be casted. Think about returning long rather then int!
822 return (long) criteria
.uniqueResult();
826 public List
<T
> list(Integer limit
, Integer start
) {
827 return list(limit
, start
, null);
831 public List
<Object
[]> group(Class
<?
extends T
> clazz
, Integer limit
, Integer start
, List
<Grouping
> groups
,
832 List
<String
> propertyPaths
) {
834 Criteria criteria
= null;
835 criteria
= criterionForType(clazz
);
837 addGroups(criteria
, groups
);
840 criteria
.setFirstResult(start
);
841 criteria
.setMaxResults(limit
);
844 @SuppressWarnings("unchecked")
845 List
<Object
[]> result
= criteria
.list();
847 if (propertyPaths
!= null && !propertyPaths
.isEmpty()) {
848 for (Object
[] objects
: result
) {
849 defaultBeanInitializer
.initialize(objects
[0], propertyPaths
);
856 protected void countGroups(DetachedCriteria criteria
, List
<Grouping
> groups
) {
857 if (groups
!= null) {
859 Map
<String
, String
> aliases
= new HashMap
<String
, String
>();
861 for (Grouping grouping
: groups
) {
862 if (grouping
.getAssociatedObj() != null) {
864 if ((alias
= aliases
.get(grouping
.getAssociatedObj())) == null) {
865 alias
= grouping
.getAssociatedObjectAlias();
866 aliases
.put(grouping
.getAssociatedObj(), alias
);
867 criteria
.createAlias(grouping
.getAssociatedObj(), alias
);
872 ProjectionList projectionList
= Projections
.projectionList();
874 for (Grouping grouping
: groups
) {
875 grouping
.addProjection(projectionList
);
877 criteria
.setProjection(projectionList
);
881 protected void addGroups(Criteria criteria
, List
<Grouping
> groups
) {
882 if (groups
!= null) {
884 Map
<String
, String
> aliases
= new HashMap
<String
, String
>();
886 for (Grouping grouping
: groups
) {
887 if (grouping
.getAssociatedObj() != null) {
889 if ((alias
= aliases
.get(grouping
.getAssociatedObj())) == null) {
890 alias
= grouping
.getAssociatedObjectAlias();
891 aliases
.put(grouping
.getAssociatedObj(), alias
);
892 criteria
.createAlias(grouping
.getAssociatedObj(), alias
);
897 ProjectionList projectionList
= Projections
.projectionList();
899 for (Grouping grouping
: groups
) {
900 grouping
.addProjection(projectionList
);
902 criteria
.setProjection(projectionList
);
904 for (Grouping grouping
: groups
) {
905 grouping
.addOrder(criteria
);
912 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
913 return list(limit
, start
, orderHints
, null);
917 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
918 Criteria criteria
= getSession().createCriteria(type
);
920 criteria
.setFirstResult(start
);
921 criteria
.setMaxResults(limit
);
924 addOrder(criteria
, orderHints
);
925 @SuppressWarnings("unchecked")
926 List
<T
> results
= criteria
.list();
928 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
933 public <S
extends T
> List
<S
> list(Class
<S
> clazz
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
,
934 List
<String
> propertyPaths
) {
935 Criteria criteria
= null;
937 criteria
= getSession().createCriteria(type
);
939 criteria
= getSession().createCriteria(clazz
);
942 addLimitAndStart(criteria
, limit
, start
);
944 addOrder(criteria
, orderHints
);
946 @SuppressWarnings("unchecked")
947 List
<S
> results
= criteria
.list();
949 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
953 public <S
extends T
> List
<S
> list(Class
<S
> type
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
954 return list(type
, limit
, start
, orderHints
, null);
958 public <S
extends T
> List
<S
> list(Class
<S
> type
, Integer limit
, Integer start
) {
959 return list(type
, limit
, start
, null, null);
963 public Class
<T
> getType() {
967 protected void setPagingParameter(Query query
, Integer pageSize
, Integer pageIndex
) {
968 if (pageSize
!= null) {
969 query
.setMaxResults(pageSize
);
970 if (pageIndex
!= null) {
971 query
.setFirstResult(pageIndex
* pageSize
);
973 query
.setFirstResult(0);
978 protected void setPagingParameter(AuditQuery query
, Integer pageSize
, Integer pageIndex
) {
979 if (pageSize
!= null) {
980 query
.setMaxResults(pageSize
);
981 if (pageIndex
!= null) {
982 query
.setFirstResult(pageIndex
* pageSize
);
984 query
.setFirstResult(0);
990 public long count(T example
, Set
<String
> includeProperties
) {
991 Criteria criteria
= getSession().createCriteria(example
.getClass());
992 addExample(criteria
, example
, includeProperties
);
994 criteria
.setProjection(Projections
.rowCount());
995 return (Long
) criteria
.uniqueResult();
998 protected void addExample(Criteria criteria
, T example
, Set
<String
> includeProperties
) {
999 if (includeProperties
!= null && !includeProperties
.isEmpty()) {
1000 criteria
.add(Example
.create(example
).setPropertySelector(new PropertySelectorImpl(includeProperties
)));
1001 ClassMetadata classMetadata
= getSession().getSessionFactory().getClassMetadata(example
.getClass());
1002 for (String property
: includeProperties
) {
1003 Type type
= classMetadata
.getPropertyType(property
);
1004 if (type
.isEntityType()) {
1006 Field field
= ReflectionUtils
.findField(example
.getClass(), property
);
1007 field
.setAccessible(true);
1008 Object value
= field
.get(example
);
1009 if (value
!= null) {
1010 criteria
.add(Restrictions
.eq(property
, value
));
1012 criteria
.add(Restrictions
.isNull(property
));
1014 } catch (SecurityException se
) {
1015 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
1017 } catch (HibernateException he
) {
1018 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
1020 } catch (IllegalArgumentException iae
) {
1021 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
1023 } catch (IllegalAccessException ie
) {
1024 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
,
1030 criteria
.add(Example
.create(example
));
1036 * NOTE: We can't reuse
1037 * {@link #list(Class, String, Object, MatchMode, Integer, Integer, List, List)
1038 * here due to different default behavior of the <code>matchmode</code>
1043 * @param queryString
1049 * @param propertyPaths
1053 public <S
extends T
> List
<S
> findByParam(Class
<S
> clazz
, String param
, String queryString
, MatchMode matchmode
,
1054 List
<Criterion
> criterion
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
1055 List
<String
> propertyPaths
) {
1056 Set
<String
> stringSet
= new HashSet
<>();
1057 stringSet
.add(param
);
1058 return this.findByParam(clazz
, stringSet
, queryString
, matchmode
,
1059 criterion
, pageSize
, pageNumber
, orderHints
,
1064 public <S
extends T
> List
<S
> findByParam(Class
<S
> clazz
, Set
<String
> params
, String queryString
, MatchMode matchmode
,
1065 List
<Criterion
> criterion
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,
1066 List
<String
> propertyPaths
) {
1068 Criteria criteria
= criterionForType(clazz
);
1070 if (queryString
!= null) {
1071 Set
<Criterion
> criterions
= new HashSet
<>();
1072 for (String param
: params
){
1074 if (matchmode
== null) {
1075 crit
= Restrictions
.ilike(param
, queryString
);
1076 } else if (matchmode
== MatchMode
.BEGINNING
) {
1077 crit
= Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.START
);
1078 } else if (matchmode
== MatchMode
.END
) {
1079 crit
= Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.END
);
1080 } else if (matchmode
== MatchMode
.EXACT
) {
1081 crit
= Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.EXACT
);
1083 crit
= Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
);
1085 criterions
.add(crit
);
1087 if (criterions
.size()>1){
1088 Iterator
<Criterion
> critIterator
= criterions
.iterator();
1089 Disjunction disjunction
= Restrictions
.disjunction();
1090 while (critIterator
.hasNext()){
1091 disjunction
.add(critIterator
.next());
1094 criteria
.add(disjunction
);
1097 if (!criterions
.isEmpty()){
1098 criteria
.add(criterions
.iterator().next());
1104 addCriteria(criteria
, criterion
);
1106 if (pageSize
!= null) {
1107 criteria
.setMaxResults(pageSize
);
1108 if (pageNumber
!= null) {
1109 criteria
.setFirstResult(pageNumber
* pageSize
);
1111 criteria
.setFirstResult(0);
1115 addOrder(criteria
, orderHints
);
1117 @SuppressWarnings("unchecked")
1118 List
<S
> result
= criteria
.list();
1119 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
1127 * @param queryString
1133 public long countByParam(Class
<?
extends T
> clazz
, String param
, String queryString
, MatchMode matchmode
,
1134 List
<Criterion
> criterion
) {
1136 Criteria criteria
= null;
1138 criteria
= criterionForType(clazz
);
1140 if (queryString
!= null) {
1141 if (matchmode
== null) {
1142 criteria
.add(Restrictions
.ilike(param
, queryString
));
1143 } else if (matchmode
== MatchMode
.BEGINNING
) {
1144 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.START
));
1145 } else if (matchmode
== MatchMode
.END
) {
1146 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.END
));
1147 } else if (matchmode
== MatchMode
.EXACT
) {
1148 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.EXACT
));
1150 criteria
.add(Restrictions
.ilike(param
, queryString
, org
.hibernate
.criterion
.MatchMode
.ANYWHERE
));
1154 addCriteria(criteria
, criterion
);
1156 criteria
.setProjection(Projections
.rowCount());
1158 return (Long
) criteria
.uniqueResult();
1162 * Creates a criteria query for the CDM <code>type</code> either for counting or listing matching entities.
1164 * The set of matching entities can be restricted by passing a list of {@link Restriction} objects.
1165 * Restrictions can logically combined:
1168 new Restriction<String>("titleCache", MatchMode.ANYWHERE, "foo"),
1169 new Restriction<String>("institute.name", Operator.OR, MatchMode.BEGINNING, "Bar")
1172 * The first Restriction in the example above by default has the <code>Operator.AND</code> which will be
1173 * ignored since this is the first restriction. The <code>Operator</code> of further restrictions in the
1174 * list are used to combine with the previous restriction.
1177 * @param restrictions
1181 protected Criteria
createCriteria(Class
<?
extends T
> type
, List
<Restriction
<?
>> restrictions
, boolean doCount
) {
1183 DetachedCriteria idsOnlyCriteria
= DetachedCriteria
.forClass(entityType(type
));
1184 idsOnlyCriteria
.setProjection(Projections
.distinct(Projections
.id()));
1186 addRestrictions(restrictions
, idsOnlyCriteria
);
1188 Criteria criteria
= criterionForType(type
);
1189 criteria
.add(Subqueries
.propertyIn("id", idsOnlyCriteria
));
1192 criteria
.setProjection(Projections
.rowCount());
1194 idsOnlyCriteria
.setProjection(Projections
.distinct(Projections
.property("id")));
1202 public <S
extends T
> List
<S
> findByParamWithRestrictions(Class
<S
> clazz
, String param
, String queryString
,
1203 MatchMode matchmode
, List
<Restriction
<?
>> restrictions
, Integer pageSize
, Integer pageNumber
,
1204 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1206 List
<Restriction
<?
>> allRestrictions
= new ArrayList
<>();
1207 allRestrictions
.add(new Restriction
<String
>(param
, matchmode
, queryString
));
1208 if(restrictions
!= null){
1209 allRestrictions
.addAll(restrictions
);
1211 Criteria criteria
= createCriteria(clazz
, allRestrictions
, false);
1213 if (pageSize
!= null) {
1214 criteria
.setMaxResults(pageSize
);
1215 if (pageNumber
!= null) {
1216 criteria
.setFirstResult(pageNumber
* pageSize
);
1218 criteria
.setFirstResult(0);
1222 addOrder(criteria
, orderHints
);
1224 @SuppressWarnings("unchecked")
1225 List
<S
> result
= criteria
.list();
1226 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
1232 public long countByParamWithRestrictions(Class
<?
extends T
> clazz
, String param
, String queryString
,
1233 MatchMode matchmode
, List
<Restriction
<?
>> restrictions
) {
1235 List
<Restriction
<?
>> allRestrictions
= new ArrayList
<>();
1236 allRestrictions
.add(new Restriction
<String
>(param
, matchmode
, queryString
));
1237 if(restrictions
!= null){
1238 allRestrictions
.addAll(restrictions
);
1240 Criteria criteria
= createCriteria(clazz
, allRestrictions
, true);
1242 return (Long
) criteria
.uniqueResult();
1246 public <S
extends T
> List
<S
> list(S example
, Set
<String
> includeProperties
, Integer limit
, Integer start
,
1247 List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
1248 Criteria criteria
= getSession().createCriteria(example
.getClass());
1249 addExample(criteria
, example
, includeProperties
);
1251 addLimitAndStart(criteria
, limit
, start
);
1253 addOrder(criteria
, orderHints
);
1255 @SuppressWarnings("unchecked")
1256 List
<S
> results
= criteria
.list();
1257 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
1261 private class PropertySelectorImpl
implements PropertySelector
{
1263 private final Set
<String
> includeProperties
;
1267 private static final long serialVersionUID
= -3175311800911570546L;
1269 public PropertySelectorImpl(Set
<String
> includeProperties
) {
1270 this.includeProperties
= includeProperties
;
1274 public boolean include(Object propertyValue
, String propertyName
, Type type
) {
1275 if (includeProperties
.contains(propertyName
)) {
1284 private class CriterionWithOperator
{
1286 Restriction
.Operator operator
;
1287 Criterion criterion
;
1290 public CriterionWithOperator(Operator operator
, Criterion criterion
) {
1292 this.operator
= operator
;
1293 this.criterion
= criterion
;
1300 * Returns a Criteria for the given {@link Class class} or, if
1301 * <code>null</code>, for the base {@link Class class} of this DAO.
1304 * @return the Criteria
1306 protected Criteria
getCriteria(Class
<?
extends CdmBase
> clazz
) {
1307 Criteria criteria
= null;
1308 if (clazz
== null) {
1309 criteria
= getSession().createCriteria(type
);
1311 criteria
= getSession().createCriteria(clazz
);
1321 protected AuditQuery
makeAuditQuery(Class
<?
extends CdmBase
> clazz
, AuditEvent auditEvent
) {
1322 AuditQuery query
= null;
1324 if (clazz
== null) {
1325 query
= getAuditReader().createQuery().forEntitiesAtRevision(type
, auditEvent
.getRevisionNumber());
1327 query
= getAuditReader().createQuery().forEntitiesAtRevision(clazz
, auditEvent
.getRevisionNumber());
1332 protected AuditReader
getAuditReader() {
1333 return AuditReaderFactory
.get(getSession());