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
.Collection
;
14 import java
.util
.HashMap
;
15 import java
.util
.Iterator
;
16 import java
.util
.List
;
19 import java
.util
.UUID
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.apache
.lucene
.search
.Sort
;
23 import org
.apache
.lucene
.search
.SortField
;
24 import org
.hibernate
.Criteria
;
25 import org
.hibernate
.HibernateException
;
26 import org
.hibernate
.LockMode
;
27 import org
.hibernate
.NonUniqueObjectException
;
28 import org
.hibernate
.Query
;
29 import org
.hibernate
.Session
;
30 import org
.hibernate
.criterion
.Criterion
;
31 import org
.hibernate
.criterion
.DetachedCriteria
;
32 import org
.hibernate
.criterion
.Example
;
33 import org
.hibernate
.criterion
.Order
;
34 import org
.hibernate
.criterion
.ProjectionList
;
35 import org
.hibernate
.criterion
.Projections
;
36 import org
.hibernate
.criterion
.Restrictions
;
37 import org
.hibernate
.criterion
.Example
.PropertySelector
;
38 import org
.hibernate
.envers
.query
.AuditQuery
;
39 import org
.hibernate
.metadata
.ClassMetadata
;
40 import org
.hibernate
.search
.FullTextQuery
;
41 import org
.hibernate
.type
.Type
;
42 import org
.joda
.time
.DateTime
;
43 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
44 import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
45 import org
.springframework
.dao
.DataAccessException
;
46 import org
.springframework
.dao
.InvalidDataAccessApiUsageException
;
47 import org
.springframework
.dao
.NonTransientDataAccessException
;
48 import org
.springframework
.dao
.UncategorizedDataAccessException
;
49 import org
.springframework
.security
.Authentication
;
50 import org
.springframework
.security
.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
.BeanInitializer
;
58 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ICdmEntityDao
;
59 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.replace
.ReferringObjectMetadata
;
60 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.replace
.ReferringObjectMetadataFactory
;
61 import eu
.etaxonomy
.cdm
.persistence
.query
.Grouping
;
62 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
67 * FIXME CdmEntityDaoBase is abstract, can it be annotated with @Repository?
70 public abstract class CdmEntityDaoBase
<T
extends CdmBase
> extends DaoBase
implements ICdmEntityDao
<T
> {
71 private static final Logger logger
= Logger
.getLogger(CdmEntityDaoBase
.class);
73 int flushAfterNo
= 1000; //large numbers may cause synchronisation errors when commiting the session !!
74 protected Class
<T
> type
;
77 @Qualifier("defaultBeanInitializer")
78 protected BeanInitializer defaultBeanInitializer
;
82 private ReferringObjectMetadataFactory referringObjectMetadataFactory
;
85 public CdmEntityDaoBase(Class
<T
> type
){
87 logger
.debug("Creating DAO of type [" + type
.getSimpleName() + "]");
90 public void lock(T t
, LockMode lockMode
) {
91 getSession().lock(t
, lockMode
);
94 public void refresh(T t
, LockMode lockMode
, List
<String
> propertyPaths
) {
95 getSession().refresh(t
, lockMode
);
96 defaultBeanInitializer
.initialize(t
, propertyPaths
);
99 //TODO this method should be moved to a concrete class (not typed)
100 public UUID
saveCdmObj(CdmBase cdmObj
) throws DataAccessException
{
101 getSession().saveOrUpdate(cdmObj
);
102 return cdmObj
.getUuid();
105 //TODO: Replace saveCdmObj() by saveCdmObject_
106 private UUID
saveCdmObject_(T cdmObj
){
107 getSession().saveOrUpdate(cdmObj
);
108 return cdmObj
.getUuid();
111 //TODO: Use everywhere CdmEntityDaoBase.saveAll() instead of ServiceBase.saveCdmObjectAll()?
112 public Map
<UUID
, T
> saveAll(Collection
<T
> cdmObjCollection
){
113 int types
= cdmObjCollection
.getClass().getTypeParameters().length
;
115 if (logger
.isDebugEnabled()){logger
.debug("ClassType: + " + cdmObjCollection
.getClass().getTypeParameters()[0]);}
118 Map
<UUID
, T
> resultMap
= new HashMap
<UUID
, T
>();
119 Iterator
<T
> iterator
= cdmObjCollection
.iterator();
121 while(iterator
.hasNext()){
122 if ( ( (i
% 2000) == 0) && (i
> 0) ){logger
.debug("Saved " + i
+ " objects" );}
123 T cdmObj
= iterator
.next();
124 UUID uuid
= saveCdmObject_(cdmObj
);
125 if (logger
.isDebugEnabled()){logger
.debug("Save cdmObj: " + (cdmObj
== null?
null: cdmObj
.toString()));}
126 resultMap
.put(uuid
, cdmObj
);
128 if ( (i
% flushAfterNo
) == 0){
131 logger
.debug("flush");
134 logger
.error("UUUIIIII");
140 if ( logger
.isInfoEnabled() ){logger
.info("Saved " + i
+ " objects" );}
144 public T
replace(T x
, T y
) {
149 Class commonClass
= x
.getClass();
151 while(!commonClass
.isAssignableFrom(y
.getClass())) {
152 if(commonClass
.equals(type
)) {
153 throw new RuntimeException();
155 commonClass
= commonClass
.getSuperclass();
159 getSession().merge(x
);
161 Set
<ReferringObjectMetadata
> referringObjectMetas
= referringObjectMetadataFactory
.get(x
.getClass());
163 for(ReferringObjectMetadata referringObjectMetadata
: referringObjectMetas
) {
165 List
<CdmBase
> referringObjects
= referringObjectMetadata
.getReferringObjects(x
,getSession());
167 for(CdmBase referringObject
: referringObjects
) {
169 referringObjectMetadata
.replace(referringObject
,x
,y
);
170 getSession().update(referringObject
);
172 } catch (IllegalArgumentException e
) {
173 throw new RuntimeException(e
.getMessage(),e
);
174 } catch (IllegalAccessException e
) {
175 throw new RuntimeException(e
.getMessage(),e
);
182 public Session
getSession() throws DataAccessException
{
183 return super.getSession();
186 public void clear() throws DataAccessException
{
187 Session session
= getSession();
189 if (logger
.isDebugEnabled()){logger
.debug("dao clear end");}
192 public UUID
merge(T transientObject
) throws DataAccessException
{
193 Session session
= getSession();
194 session
.merge(transientObject
);
195 if (logger
.isDebugEnabled()){logger
.debug("dao merge end");}
196 return transientObject
.getUuid();
199 public UUID
saveOrUpdate(T transientObject
) throws DataAccessException
{
201 if (logger
.isDebugEnabled()){logger
.debug("dao saveOrUpdate start...");}
202 if (logger
.isDebugEnabled()){logger
.debug("transientObject(" + transientObject
.getClass().getSimpleName() + ") ID:" + transientObject
.getId() + ", UUID: " + transientObject
.getUuid()) ;}
203 Session session
= getSession();
204 if(transientObject
.getId() != 0 && VersionableEntity
.class.isAssignableFrom(transientObject
.getClass())) {
205 VersionableEntity versionableEntity
= (VersionableEntity
)transientObject
;
206 versionableEntity
.setUpdated(new DateTime());
207 Authentication authentication
= SecurityContextHolder
.getContext().getAuthentication();
208 if(authentication
!= null && authentication
.getPrincipal() != null && authentication
.getPrincipal() instanceof User
) {
209 User user
= (User
)authentication
.getPrincipal();
210 versionableEntity
.setUpdatedBy(user
);
213 session
.saveOrUpdate(transientObject
);
214 if (logger
.isDebugEnabled()){logger
.debug("dao saveOrUpdate end");}
215 return transientObject
.getUuid();
216 } catch (NonUniqueObjectException e
) {
217 logger
.error("Error in CdmEntityDaoBase.saveOrUpdate(obj)");
218 logger
.error(e
.getIdentifier());
219 logger
.error(e
.getEntityName());
220 logger
.error(e
.getMessage());
224 } catch (HibernateException e
) {
231 public UUID
save(T newInstance
) throws DataAccessException
{
232 getSession().save(newInstance
);
233 return newInstance
.getUuid();
236 public UUID
update(T transientObject
) throws DataAccessException
{
237 getSession().update(transientObject
);
238 return transientObject
.getUuid();
241 public UUID
refresh(T persistentObject
) throws DataAccessException
{
242 getSession().refresh(persistentObject
);
243 return persistentObject
.getUuid();
246 public UUID
delete(T persistentObject
) throws DataAccessException
{
247 if (persistentObject
== null){
248 logger
.warn(type
.getName() + " was 'null'");
252 // Merge the object in if it is detached
254 // I think this is preferable to catching lazy initialization errors
255 // as that solution only swallows and hides the exception, but doesn't
256 // actually solve it.
257 getSession().merge(persistentObject
);
258 getSession().delete(persistentObject
);
259 return persistentObject
.getUuid();
262 public T
findById(int id
) throws DataAccessException
{
263 return (T
) getSession().get(type
, id
);
266 public T
findByUuid(UUID uuid
) throws DataAccessException
{
267 Session session
= getSession();
268 Criteria crit
= session
.createCriteria(type
);
269 crit
.add(Restrictions
.eq("uuid", uuid
));
270 crit
.addOrder(Order
.desc("created"));
271 List
<T
> results
= crit
.list();
272 if (results
.isEmpty()){
275 if(results
.size() > 1){
276 logger
.error("findByUuid() delivers more than one result for UUID: " + uuid
);
278 return results
.get(0);
282 public List
<T
> findByUuid(Set
<UUID
> uuidSet
) throws DataAccessException
{
283 Session session
= getSession();
284 String hql
= "from " + type
.getSimpleName() + " type where type.uuid in ( :uuidSet )" ;
285 Query query
= session
.createQuery(hql
);
286 query
.setParameterList("uuidSet", uuidSet
);
287 List
<T
> results
= query
.list();
291 public T
load(UUID uuid
) {
292 T bean
= findByUuid(uuid
);
295 defaultBeanInitializer
.load(bean
);
301 public T
load(UUID uuid
, List
<String
> propertyPaths
){
302 T bean
= findByUuid(uuid
);
306 defaultBeanInitializer
.initialize(bean
, propertyPaths
);
311 public List
<T
> load(Set
<UUID
> uuidSet
, List
<String
> propertyPaths
) throws DataAccessException
{
312 List
<T
> list
= findByUuid(uuidSet
);
313 defaultBeanInitializer
.initializeAll(list
, propertyPaths
);
317 public Boolean
exists(UUID uuid
) {
318 if (findByUuid(uuid
)==null){
328 public int count(Class
<?
extends T
> clazz
) {
329 Session session
= getSession();
330 Criteria criteria
= null;
332 criteria
= session
.createCriteria(type
);
334 criteria
= session
.createCriteria(clazz
);
336 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
337 return (Integer
) criteria
.uniqueResult();
340 public List
<T
> list(Integer limit
, Integer start
) {
341 return list(limit
, start
, null);
344 public List
<Object
[]> group(Class
<?
extends T
> clazz
,Integer limit
, Integer start
, List
<Grouping
> groups
, List
<String
> propertyPaths
) {
346 Criteria criteria
= null;
348 criteria
= getSession().createCriteria(type
);
350 criteria
= getSession().createCriteria(clazz
);
353 addGroups(criteria
,groups
);
356 criteria
.setFirstResult(start
);
357 criteria
.setMaxResults(limit
);
360 List
<Object
[]> result
= (List
<Object
[]>)criteria
.list();
362 if(propertyPaths
!= null && !propertyPaths
.isEmpty()) {
363 for(Object
[] objects
: result
) {
364 defaultBeanInitializer
.initialize(objects
[0], propertyPaths
);
371 protected void countGroups(DetachedCriteria criteria
,List
<Grouping
> groups
) {
375 Map
<String
,String
> aliases
= new HashMap
<String
,String
>();
377 for(Grouping grouping
: groups
) {
378 if(grouping
.getAssociatedObj() != null) {
380 if((alias
= aliases
.get(grouping
.getAssociatedObj())) == null) {
381 alias
= grouping
.getAssociatedObjectAlias();
382 aliases
.put(grouping
.getAssociatedObj(), alias
);
383 criteria
.createAlias(grouping
.getAssociatedObj(),alias
);
388 ProjectionList projectionList
= Projections
.projectionList();
390 for(Grouping grouping
: groups
) {
391 grouping
.addProjection(projectionList
);
393 criteria
.setProjection(projectionList
);
397 protected void addGroups(Criteria criteria
,List
<Grouping
> groups
) {
401 Map
<String
,String
> aliases
= new HashMap
<String
,String
>();
403 for(Grouping grouping
: groups
) {
404 if(grouping
.getAssociatedObj() != null) {
406 if((alias
= aliases
.get(grouping
.getAssociatedObj())) == null) {
407 alias
= grouping
.getAssociatedObjectAlias();
408 aliases
.put(grouping
.getAssociatedObj(), alias
);
409 criteria
.createAlias(grouping
.getAssociatedObj(),alias
);
414 ProjectionList projectionList
= Projections
.projectionList();
416 for(Grouping grouping
: groups
) {
417 grouping
.addProjection(projectionList
);
419 criteria
.setProjection(projectionList
);
421 for(Grouping grouping
: groups
) {
422 grouping
.addOrder(criteria
);
428 protected void addCriteria(Criteria criteria
, List
<Criterion
> criterion
) {
429 if(criterion
!= null) {
430 for(Criterion c
: criterion
) {
437 protected void addOrder(FullTextQuery fullTextQuery
, List
<OrderHint
> orderHints
) {
438 if(orderHints
!= null && !orderHints
.isEmpty()) {
439 org
.apache
.lucene
.search
.Sort sort
= new Sort();
440 SortField
[] sortFields
= new SortField
[orderHints
.size()];
441 for(int i
= 0; i
< orderHints
.size(); i
++) {
442 OrderHint orderHint
= orderHints
.get(i
);
443 switch(orderHint
.getSortOrder()) {
445 sortFields
[i
] = new SortField(orderHint
.getPropertyName() + "_forSort", true);
447 sortFields
[i
] = new SortField(orderHint
.getPropertyName() + "_forSort",false);
450 sort
.setSort(sortFields
);
451 fullTextQuery
.setSort(sort
);
455 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
456 return list(limit
,start
,orderHints
,null);
459 public List
<T
> list(Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
460 Criteria criteria
= getSession().createCriteria(type
);
462 criteria
.setFirstResult(start
);
463 criteria
.setMaxResults(limit
);
466 addOrder(criteria
,orderHints
);
467 List
<T
> results
= (List
<T
>)criteria
.list();
469 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
473 public List
<T
> list(Class
<?
extends T
> clazz
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
474 Criteria criteria
= null;
476 criteria
= getSession().createCriteria(type
);
478 criteria
= getSession().createCriteria(clazz
);
483 criteria
.setFirstResult(start
);
485 criteria
.setFirstResult(0);
487 criteria
.setMaxResults(limit
);
490 addOrder(criteria
,orderHints
);
492 List
<T
> results
= (List
<T
>)criteria
.list();
493 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
497 public List
<T
> list(Class
<?
extends T
> type
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
) {
498 return list(type
,limit
,start
,orderHints
,null);
501 public List
<T
> list(Class
<?
extends T
> type
, Integer limit
, Integer start
) {
502 return list(type
,limit
,start
,null,null);
505 public List
<T
> rows(String tableName
, int limit
, int start
) {
506 Query query
= getSession().createQuery("from " + tableName
+ " order by uuid");
507 query
.setFirstResult(start
);
508 query
.setMaxResults(limit
);
509 List
<T
> result
= query
.list();
513 public Class
<T
> getType() {
517 protected void setPagingParameter(Query query
, Integer pageSize
, Integer pageNumber
){
518 if(pageSize
!= null) {
519 query
.setMaxResults(pageSize
);
520 if(pageNumber
!= null) {
521 query
.setFirstResult(pageNumber
* pageSize
);
523 query
.setFirstResult(0);
528 protected void setPagingParameter(AuditQuery query
, Integer pageSize
, Integer pageNumber
){
529 if(pageSize
!= null) {
530 query
.setMaxResults(pageSize
);
531 if(pageNumber
!= null) {
532 query
.setFirstResult(pageNumber
* pageSize
);
534 query
.setFirstResult(0);
539 public int count(T example
, Set
<String
> includeProperties
) {
540 Criteria criteria
= getSession().createCriteria(example
.getClass());
541 addExample(criteria
,example
,includeProperties
);
543 criteria
.setProjection(Projections
.rowCount());
544 return (Integer
)criteria
.uniqueResult();
547 protected void addExample(Criteria criteria
, T example
, Set
<String
> includeProperties
) {
548 if(includeProperties
!= null && !includeProperties
.isEmpty()) {
549 criteria
.add(Example
.create(example
).setPropertySelector(new PropertySelectorImpl(includeProperties
)));
550 ClassMetadata classMetadata
= getSession().getSessionFactory().getClassMetadata(example
.getClass());
551 for(String property
: includeProperties
) {
552 Type type
= classMetadata
.getPropertyType(property
);
553 if(type
.isEntityType()) {
555 Field field
= ReflectionUtils
.findField(example
.getClass(), property
);
556 field
.setAccessible(true);
557 Object value
= field
.get(example
);
559 criteria
.add(Restrictions
.eq(property
,value
));
561 criteria
.add(Restrictions
.isNull(property
));
563 } catch (SecurityException se
) {
564 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
, se
);
565 } catch (HibernateException he
) {
566 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
, he
);
567 } catch (IllegalArgumentException iae
) {
568 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
, iae
);
569 } catch (IllegalAccessException ie
) {
570 throw new InvalidDataAccessApiUsageException("Tried to add criteria for property " + property
, ie
);
576 criteria
.add(Example
.create(example
));
580 public List
<T
> list(T example
, Set
<String
> includeProperties
, Integer limit
, Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
581 Criteria criteria
= getSession().createCriteria(example
.getClass());
582 addExample(criteria
,example
,includeProperties
);
586 criteria
.setFirstResult(start
);
588 criteria
.setFirstResult(0);
590 criteria
.setMaxResults(limit
);
593 addOrder(criteria
,orderHints
);
595 List
<T
> results
= (List
<T
>)criteria
.list();
596 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
600 private class PropertySelectorImpl
implements PropertySelector
{
602 private Set
<String
> includeProperties
;
606 private static final long serialVersionUID
= -3175311800911570546L;
608 public PropertySelectorImpl(Set
<String
> includeProperties
) {
609 this.includeProperties
= includeProperties
;
612 public boolean include(Object propertyValue
, String propertyName
, Type type
) {
613 if(includeProperties
.contains(propertyName
)) {