651400304bf2c47e2c588267cabb67133ac9f517
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / common / CdmEntityDaoBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.persistence.dao.hibernate.common;
11
12 import java.lang.reflect.Field;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.UUID;
20
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;
53
54 import eu.etaxonomy.cdm.model.common.CdmBase;
55 import eu.etaxonomy.cdm.model.common.User;
56 import eu.etaxonomy.cdm.model.common.VersionableEntity;
57 import eu.etaxonomy.cdm.persistence.dao.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;
63
64
65 /**
66 * @author a.mueller
67 * FIXME CdmEntityDaoBase is abstract, can it be annotated with @Repository?
68 */
69 @Repository
70 public abstract class CdmEntityDaoBase<T extends CdmBase> extends DaoBase implements ICdmEntityDao<T> {
71 private static final Logger logger = Logger.getLogger(CdmEntityDaoBase.class);
72
73 int flushAfterNo = 1000; //large numbers may cause synchronisation errors when commiting the session !!
74 protected Class<T> type;
75
76 @Autowired
77 @Qualifier("defaultBeanInitializer")
78 protected BeanInitializer defaultBeanInitializer;
79
80
81 @Autowired
82 private ReferringObjectMetadataFactory referringObjectMetadataFactory;
83
84
85 public CdmEntityDaoBase(Class<T> type){
86 this.type = type;
87 logger.debug("Creating DAO of type [" + type.getSimpleName() + "]");
88 }
89
90 public void lock(T t, LockMode lockMode) {
91 getSession().lock(t, lockMode);
92 }
93
94 public void refresh(T t, LockMode lockMode, List<String> propertyPaths) {
95 getSession().refresh(t, lockMode);
96 defaultBeanInitializer.initialize(t, propertyPaths);
97 }
98
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();
103 }
104
105 //TODO: Replace saveCdmObj() by saveCdmObject_
106 private UUID saveCdmObject_(T cdmObj){
107 getSession().saveOrUpdate(cdmObj);
108 return cdmObj.getUuid();
109 }
110
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;
114 if (types > 0){
115 if (logger.isDebugEnabled()){logger.debug("ClassType: + " + cdmObjCollection.getClass().getTypeParameters()[0]);}
116 }
117
118 Map<UUID, T> resultMap = new HashMap<UUID, T>();
119 Iterator<T> iterator = cdmObjCollection.iterator();
120 int i = 0;
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);
127 i++;
128 if ( (i % flushAfterNo) == 0){
129 try{
130 //TODO: fixme!!
131 logger.debug("flush");
132 flush();
133 }catch(Exception e){
134 logger.error("UUUIIIII");
135 e.printStackTrace();
136 }
137 }
138 }
139
140 if ( logger.isInfoEnabled() ){logger.info("Saved " + i + " objects" );}
141 return resultMap;
142 }
143
144 public T replace(T x, T y) {
145 if(x.equals(y)) {
146 return y;
147 }
148
149 Class commonClass = x.getClass();
150 if(y != null) {
151 while(!commonClass.isAssignableFrom(y.getClass())) {
152 if(commonClass.equals(type)) {
153 throw new RuntimeException();
154 }
155 commonClass = commonClass.getSuperclass();
156 }
157 }
158
159 getSession().merge(x);
160
161 Set<ReferringObjectMetadata> referringObjectMetas = referringObjectMetadataFactory.get(x.getClass());
162
163 for(ReferringObjectMetadata referringObjectMetadata : referringObjectMetas) {
164
165 List<CdmBase> referringObjects = referringObjectMetadata.getReferringObjects(x,getSession());
166
167 for(CdmBase referringObject : referringObjects) {
168 try {
169 referringObjectMetadata.replace(referringObject,x,y);
170 getSession().update(referringObject);
171
172 } catch (IllegalArgumentException e) {
173 throw new RuntimeException(e.getMessage(),e);
174 } catch (IllegalAccessException e) {
175 throw new RuntimeException(e.getMessage(),e);
176 }
177 }
178 }
179 return y;
180 }
181
182 public Session getSession() throws DataAccessException {
183 return super.getSession();
184 }
185
186 public void clear() throws DataAccessException {
187 Session session = getSession();
188 session.clear();
189 if (logger.isDebugEnabled()){logger.debug("dao clear end");}
190 }
191
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();
197 }
198
199 public UUID saveOrUpdate(T transientObject) throws DataAccessException {
200 try {
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);
211 }
212 }
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());
221
222 e.printStackTrace();
223 throw e;
224 } catch (HibernateException e) {
225
226 e.printStackTrace();
227 throw e;
228 }
229 }
230
231 public UUID save(T newInstance) throws DataAccessException {
232 getSession().save(newInstance);
233 return newInstance.getUuid();
234 }
235
236 public UUID update(T transientObject) throws DataAccessException {
237 getSession().update(transientObject);
238 return transientObject.getUuid();
239 }
240
241 public UUID refresh(T persistentObject) throws DataAccessException {
242 getSession().refresh(persistentObject);
243 return persistentObject.getUuid();
244 }
245
246 public UUID delete(T persistentObject) throws DataAccessException {
247 if (persistentObject == null){
248 logger.warn(type.getName() + " was 'null'");
249 return null;
250 }
251
252 // Merge the object in if it is detached
253 //
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();
260 }
261
262 public T findById(int id) throws DataAccessException {
263 return (T) getSession().get(type, id);
264 }
265
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()){
273 return null;
274 }else{
275 if(results.size() > 1){
276 logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
277 }
278 return results.get(0);
279 }
280 }
281
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();
288 return results;
289 }
290
291 public T load(UUID uuid) {
292 T bean = findByUuid(uuid);
293 if(bean == null)
294 return null;
295 defaultBeanInitializer.load(bean);
296
297 return bean;
298 }
299
300
301 public T load(UUID uuid, List<String> propertyPaths){
302 T bean = findByUuid(uuid);
303 if(bean == null)
304 return bean;
305
306 defaultBeanInitializer.initialize(bean, propertyPaths);
307
308 return bean;
309 }
310
311 public List<T> load(Set<UUID> uuidSet, List<String> propertyPaths) throws DataAccessException{
312 List<T> list = findByUuid(uuidSet);
313 defaultBeanInitializer.initializeAll(list, propertyPaths);
314 return list;
315 }
316
317 public Boolean exists(UUID uuid) {
318 if (findByUuid(uuid)==null){
319 return false;
320 }
321 return true;
322 }
323
324 public int count() {
325 return count(type);
326 }
327
328 public int count(Class<? extends T> clazz) {
329 Session session = getSession();
330 Criteria criteria = null;
331 if(clazz == null) {
332 criteria = session.createCriteria(type);
333 } else {
334 criteria = session.createCriteria(clazz);
335 }
336 criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
337 return (Integer) criteria.uniqueResult();
338 }
339
340 public List<T> list(Integer limit, Integer start) {
341 return list(limit, start, null);
342 }
343
344 public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {
345
346 Criteria criteria = null;
347 if(clazz == null){
348 criteria = getSession().createCriteria(type);
349 } else {
350 criteria = getSession().createCriteria(clazz);
351 }
352
353 addGroups(criteria,groups);
354
355 if(limit != null) {
356 criteria.setFirstResult(start);
357 criteria.setMaxResults(limit);
358 }
359
360 List<Object[]> result = (List<Object[]>)criteria.list();
361
362 if(propertyPaths != null && !propertyPaths.isEmpty()) {
363 for(Object[] objects : result) {
364 defaultBeanInitializer.initialize(objects[0], propertyPaths);
365 }
366 }
367
368 return result;
369 }
370
371 protected void countGroups(DetachedCriteria criteria,List<Grouping> groups) {
372 if(groups != null){
373
374
375 Map<String,String> aliases = new HashMap<String,String>();
376
377 for(Grouping grouping : groups) {
378 if(grouping.getAssociatedObj() != null) {
379 String alias = 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);
384 }
385 }
386 }
387
388 ProjectionList projectionList = Projections.projectionList();
389
390 for(Grouping grouping : groups) {
391 grouping.addProjection(projectionList);
392 }
393 criteria.setProjection(projectionList);
394 }
395 }
396
397 protected void addGroups(Criteria criteria,List<Grouping> groups) {
398 if(groups != null){
399
400
401 Map<String,String> aliases = new HashMap<String,String>();
402
403 for(Grouping grouping : groups) {
404 if(grouping.getAssociatedObj() != null) {
405 String alias = 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);
410 }
411 }
412 }
413
414 ProjectionList projectionList = Projections.projectionList();
415
416 for(Grouping grouping : groups) {
417 grouping.addProjection(projectionList);
418 }
419 criteria.setProjection(projectionList);
420
421 for(Grouping grouping : groups) {
422 grouping.addOrder(criteria);
423
424 }
425 }
426 }
427
428 protected void addCriteria(Criteria criteria, List<Criterion> criterion) {
429 if(criterion != null) {
430 for(Criterion c : criterion) {
431 criteria.add(c);
432 }
433 }
434
435 }
436
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()) {
444 case ASCENDING:
445 sortFields[i] = new SortField(orderHint.getPropertyName() + "_forSort", true);
446 case DESCENDING:
447 sortFields[i] = new SortField(orderHint.getPropertyName() + "_forSort",false);
448 }
449 }
450 sort.setSort(sortFields);
451 fullTextQuery.setSort(sort);
452 }
453 }
454
455 public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints) {
456 return list(limit,start,orderHints,null);
457 }
458
459 public List<T> list(Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
460 Criteria criteria = getSession().createCriteria(type);
461 if(limit != null) {
462 criteria.setFirstResult(start);
463 criteria.setMaxResults(limit);
464 }
465
466 addOrder(criteria,orderHints);
467 List<T> results = (List<T>)criteria.list();
468
469 defaultBeanInitializer.initializeAll(results, propertyPaths);
470 return results;
471 }
472
473 public List<T> list(Class<? extends T> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
474 Criteria criteria = null;
475 if(clazz == null) {
476 criteria = getSession().createCriteria(type);
477 } else {
478 criteria = getSession().createCriteria(clazz);
479 }
480
481 if(limit != null) {
482 if(start != null) {
483 criteria.setFirstResult(start);
484 } else {
485 criteria.setFirstResult(0);
486 }
487 criteria.setMaxResults(limit);
488 }
489
490 addOrder(criteria,orderHints);
491
492 List<T> results = (List<T>)criteria.list();
493 defaultBeanInitializer.initializeAll(results, propertyPaths);
494 return results;
495 }
496
497 public List<T> list(Class<? extends T> type, Integer limit, Integer start, List<OrderHint> orderHints) {
498 return list(type,limit,start,orderHints,null);
499 }
500
501 public List<T> list(Class<? extends T> type, Integer limit, Integer start) {
502 return list(type,limit,start,null,null);
503 }
504
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();
510 return result;
511 }
512
513 public Class<T> getType() {
514 return type;
515 }
516
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);
522 } else {
523 query.setFirstResult(0);
524 }
525 }
526 }
527
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);
533 } else {
534 query.setFirstResult(0);
535 }
536 }
537 }
538
539 public int count(T example, Set<String> includeProperties) {
540 Criteria criteria = getSession().createCriteria(example.getClass());
541 addExample(criteria,example,includeProperties);
542
543 criteria.setProjection(Projections.rowCount());
544 return (Integer)criteria.uniqueResult();
545 }
546
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()) {
554 try {
555 Field field = ReflectionUtils.findField(example.getClass(), property);
556 field.setAccessible(true);
557 Object value = field.get(example);
558 if(value != null) {
559 criteria.add(Restrictions.eq(property,value));
560 } else {
561 criteria.add(Restrictions.isNull(property));
562 }
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);
571 }
572
573 }
574 }
575 } else {
576 criteria.add(Example.create(example));
577 }
578 }
579
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);
583
584 if(limit != null) {
585 if(start != null) {
586 criteria.setFirstResult(start);
587 } else {
588 criteria.setFirstResult(0);
589 }
590 criteria.setMaxResults(limit);
591 }
592
593 addOrder(criteria,orderHints);
594
595 List<T> results = (List<T>)criteria.list();
596 defaultBeanInitializer.initializeAll(results, propertyPaths);
597 return results;
598 }
599
600 private class PropertySelectorImpl implements PropertySelector {
601
602 private Set<String> includeProperties;
603 /**
604 *
605 */
606 private static final long serialVersionUID = -3175311800911570546L;
607
608 public PropertySelectorImpl(Set<String> includeProperties) {
609 this.includeProperties = includeProperties;
610 }
611
612 public boolean include(Object propertyValue, String propertyName, Type type) {
613 if(includeProperties.contains(propertyName)) {
614 return true;
615 } else {
616 return false;
617 }
618 }
619
620 }
621 }