reintegrate cdm-3.3 branch into trunk
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / hibernate / common / IdentifiableDaoBase.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.util.ArrayList;
13 import java.util.List;
14 import java.util.UUID;
15
16 import org.apache.log4j.Logger;
17 import org.apache.lucene.analysis.standard.StandardAnalyzer;
18 import org.apache.lucene.queryParser.ParseException;
19 import org.apache.lucene.queryParser.QueryParser;
20 import org.hibernate.Criteria;
21 import org.hibernate.Query;
22 import org.hibernate.Session;
23 import org.hibernate.criterion.Criterion;
24 import org.hibernate.criterion.Order;
25 import org.hibernate.criterion.Projections;
26 import org.hibernate.criterion.Restrictions;
27 import org.hibernate.envers.query.AuditEntity;
28 import org.hibernate.envers.query.AuditQuery;
29 import org.hibernate.search.FullTextSession;
30 import org.hibernate.search.Search;
31 import org.hibernate.search.SearchFactory;
32
33 import eu.etaxonomy.cdm.model.common.CdmBase;
34 import eu.etaxonomy.cdm.model.common.Credit;
35 import eu.etaxonomy.cdm.model.common.IIdentifiableEntity;
36 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
37 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
38 import eu.etaxonomy.cdm.model.common.LSID;
39 import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
40 import eu.etaxonomy.cdm.model.media.Rights;
41 import eu.etaxonomy.cdm.persistence.dao.QueryParseException;
42 import eu.etaxonomy.cdm.persistence.dao.common.IIdentifiableDao;
43 import eu.etaxonomy.cdm.persistence.query.MatchMode;
44 import eu.etaxonomy.cdm.persistence.query.OrderHint;
45
46
47 public class IdentifiableDaoBase<T extends IdentifiableEntity> extends AnnotatableDaoImpl<T> implements IIdentifiableDao<T>{
48 @SuppressWarnings("unused")
49 private static final Logger logger = Logger.getLogger(IdentifiableDaoBase.class);
50 protected String defaultField = "titleCache_tokenized";
51 protected Class<? extends T> indexedClasses[];
52
53
54
55 public IdentifiableDaoBase(Class<T> type) {
56 super(type);
57 }
58
59 /* (non-Javadoc)
60 * @see eu.etaxonomy.cdm.persistence.dao.common.ITitledDao#findByTitle(java.lang.String)
61 */
62 public List<T> findByTitle(String queryString) {
63 return findByTitle(queryString, null);
64 }
65
66 /* (non-Javadoc)
67 * @see eu.etaxonomy.cdm.persistence.dao.common.ITitledDao#findByTitle(java.lang.String)
68 */
69 public List<T> findByTitle(String queryString, CdmBase sessionObject) {
70 /**
71 * FIXME why do we need to call update in a find* method? I don't know for sure
72 * that this is a good idea . . .
73 */
74 Session session = getSession();
75 if ( sessionObject != null ) {
76 session.update(sessionObject);
77 }
78 checkNotInPriorView("IdentifiableDaoBase.findByTitle(String queryString, CdmBase sessionObject)");
79 Criteria crit = session.createCriteria(type);
80 crit.add(Restrictions.ilike("titleCache", queryString));
81 List<T> results = crit.list();
82 List<String> propertyPaths = null;
83 defaultBeanInitializer.initializeAll(results, propertyPaths);
84 return results;
85 }
86
87 public List<T> findByTitleAndClass(String queryString, Class<T> clazz) {
88 checkNotInPriorView("IdentifiableDaoBase.findByTitleAndClass(String queryString, Class<T> clazz)");
89 Criteria crit = getSession().createCriteria(clazz);
90 crit.add(Restrictions.ilike("titleCache", queryString));
91 List<T> results = crit.list();
92 return results;
93 }
94
95 public List<T> findTitleCache(Class<? extends T> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, MatchMode matchMode){
96
97 Query query = prepareFindTitleCache(clazz, queryString, pageSize,
98 pageNumber, matchMode, false);
99 List<T> result = (List<T>)query.list();
100 return result;
101 }
102
103 @Override
104 public Long countTitleCache(Class<? extends T> clazz, String queryString, MatchMode matchMode){
105
106 Query query = prepareFindTitleCache(clazz, queryString, null,
107 null, matchMode, true);
108 Long result = (Long)query.uniqueResult();
109 return result;
110 }
111
112 /**
113 * @param clazz filter by class - can be null to include all instances of type T
114 * @param queryString the query string to filter by
115 * @param pageSize
116 * @param pageNumber
117 * @param matchmode use a particular type of matching (can be null - defaults to exact matching)
118 * @return
119 */
120 private Query prepareFindTitleCache(Class<? extends T> clazz,
121 String queryString, Integer pageSize, Integer pageNumber,
122 MatchMode matchMode, boolean doCount) {
123 if(clazz == null) {
124 clazz = type;
125 }
126
127 String what = (doCount ? "count(distinct e.titleCache)": "distinct e.titleCache");
128
129 if(matchMode != null){
130 queryString = matchMode.queryStringFrom(queryString);
131 }
132 String hql = "select " + what + " from " + clazz.getName() + " e where e.titleCache like '" + queryString + "'";
133
134 Query query = getSession().createQuery(hql);
135
136 if(pageSize != null && !doCount) {
137 query.setMaxResults(pageSize);
138 if(pageNumber != null) {
139 query.setFirstResult(pageNumber * pageSize);
140 }
141 }
142 return query;
143 }
144
145
146 public List<T> findByTitle(Class<? extends T> clazz, String queryString, MatchMode matchmode, List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
147 return findByParam(clazz, "titleCache", queryString, matchmode, criterion, pageSize, pageNumber, orderHints, propertyPaths);
148 }
149
150 public List<T> findByReferenceTitle(Class<? extends T> clazz, String queryString, MatchMode matchmode, List<Criterion> criterion, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
151 return findByParam(clazz, "title", queryString, matchmode, criterion, pageSize, pageNumber, orderHints, propertyPaths);
152 }
153
154 /* (non-Javadoc)
155 * @see eu.etaxonomy.cdm.persistence.dao.common.ITitledDao#findByTitle(java.lang.String, boolean, int, int, java.util.List)
156 */
157 public List<T> findByTitle(String queryString, MatchMode matchmode, int page, int pagesize, List<Criterion> criteria) {
158 checkNotInPriorView("IdentifiableDaoBase.findByTitle(String queryString, MATCH_MODE matchmode, int page, int pagesize, List<Criterion> criteria)");
159 Criteria crit = getSession().createCriteria(type);
160 if (matchmode == MatchMode.EXACT) {
161 crit.add(Restrictions.eq("titleCache", matchmode.queryStringFrom(queryString)));
162 } else {
163 // crit.add(Restrictions.ilike("titleCache", matchmode.queryStringFrom(queryString)));
164 crit.add(Restrictions.like("titleCache", matchmode.queryStringFrom(queryString)));
165 }
166 if (pagesize >= 0) {
167 crit.setMaxResults(pagesize);
168 }
169 if(criteria != null){
170 for (Criterion criterion : criteria) {
171 crit.add(criterion);
172 }
173 }
174 crit.addOrder(Order.asc("titleCache"));
175 int firstItem = (page - 1) * pagesize;
176 crit.setFirstResult(firstItem);
177 List<T> results = crit.list();
178 List<String> propertyPaths = null;
179 defaultBeanInitializer.initializeAll(results, propertyPaths);
180 return results;
181 }
182
183 public int countRights(T identifiableEntity) {
184 checkNotInPriorView("IdentifiableDaoBase.countRights(T identifiableEntity)");
185 Query query = getSession().createQuery("select count(rights) from " + type.getSimpleName() + " identifiableEntity join identifiableEntity.rights rights where identifiableEntity = :identifiableEntity");
186 query.setParameter("identifiableEntity",identifiableEntity);
187 return ((Long)query.uniqueResult()).intValue();
188 }
189
190 public int countSources(T identifiableEntity) {
191 checkNotInPriorView("IdentifiableDaoBase.countSources(T identifiableEntity)");
192 Query query = getSession().createQuery("select count(source) from OriginalSourceBase source where source.sourcedObj = :identifiableEntity");
193 query.setParameter("identifiableEntity",identifiableEntity);
194 return ((Long)query.uniqueResult()).intValue();
195 }
196
197 public List<Rights> getRights(T identifiableEntity, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
198 checkNotInPriorView("IdentifiableDaoBase.getRights(T identifiableEntity, Integer pageSize, Integer pageNumber, List<String> propertyPaths)");
199 Query query = getSession().createQuery("select rights from " + type.getSimpleName() + " identifiableEntity join identifiableEntity.rights rights where identifiableEntity = :identifiableEntity");
200 query.setParameter("identifiableEntity",identifiableEntity);
201 setPagingParameter(query, pageSize, pageNumber);
202 List<Rights> results = (List<Rights>)query.list();
203 defaultBeanInitializer.initializeAll(results, propertyPaths);
204 return results;
205 }
206
207 public List<Credit> getCredits(T identifiableEntity, Integer pageSize, Integer pageNumber) {
208 checkNotInPriorView("IdentifiableDaoBase.getCredits(T identifiableEntity, Integer pageSize, Integer pageNumber)");
209 Query query = getSession().createQuery("select credits from " + type.getSimpleName() + " identifiableEntity join identifiableEntity.credits credits where identifiableEntity = :identifiableEntity");
210 query.setParameter("identifiableEntity",identifiableEntity);
211 setPagingParameter(query, pageSize, pageNumber);
212 return (List<Credit>)query.list();
213 }
214
215 public List<IdentifiableSource> getSources(T identifiableEntity, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
216 checkNotInPriorView("IdentifiableDaoBase.getSources(T identifiableEntity, Integer pageSize, Integer pageNumber)");
217 Query query = getSession().createQuery("select source from OriginalSourceBase source where source.sourcedObj.id = :id and source.sourcedObj.class = :class");
218 query.setParameter("id",identifiableEntity.getId());
219 query.setParameter("class",identifiableEntity.getClass().getName());
220 setPagingParameter(query, pageSize, pageNumber);
221 List<IdentifiableSource> results = (List<IdentifiableSource>)query.list();
222 defaultBeanInitializer.initializeAll(results, propertyPaths);
223 return results;
224 }
225
226 public List<T> findOriginalSourceByIdInSource(String idInSource, String idNamespace) {
227 checkNotInPriorView("IdentifiableDaoBase.findOriginalSourceByIdInSource(String idInSource, String idNamespace)");
228 Query query = getSession().createQuery(
229 "Select c from " + type.getSimpleName() + " as c " +
230 "inner join c.sources as source " +
231 "where source.idInSource = :idInSource " +
232 " AND source.idNamespace = :idNamespace"
233 );
234 query.setString("idInSource", idInSource);
235 query.setString("idNamespace", idNamespace);
236 //TODO integrate reference in where
237 return (List<T>)query.list();
238 }
239
240 public T find(LSID lsid) {
241 checkNotInPriorView("IdentifiableDaoBase.find(LSID lsid)");
242 Criteria criteria = getSession().createCriteria(type);
243 criteria.add(Restrictions.eq("lsid.authority", lsid.getAuthority()));
244 criteria.add(Restrictions.eq("lsid.namespace", lsid.getNamespace()));
245 criteria.add(Restrictions.eq("lsid.object", lsid.getObject()));
246
247 if(lsid.getRevision() != null) {
248 criteria.add(Restrictions.eq("lsid.revision", lsid.getRevision()));
249 }
250
251 T object = (T)criteria.uniqueResult();
252 if(object != null) {
253 return object;
254 } else {
255 AuditQuery query = getAuditReader().createQuery().forRevisionsOfEntity(type, false, true);
256 query.add(AuditEntity.property("lsid_authority").eq(lsid.getAuthority()));
257 query.add(AuditEntity.property("lsid_namespace").eq(lsid.getNamespace()));
258 query.add(AuditEntity.property("lsid_object").eq(lsid.getObject()));
259
260 if(lsid.getRevision() != null) {
261 query.add(AuditEntity.property("lsid_revision").eq(lsid.getRevision()));
262 }
263
264 query.addOrder(AuditEntity.revisionNumber().asc());
265 query.setMaxResults(1);
266 query.setFirstResult(0);
267 List<Object[]> objs = (List<Object[]>)query.getResultList();
268 if(objs.isEmpty()) {
269 return null;
270 } else {
271 return (T)objs.get(0)[0];
272 }
273 }
274 }
275
276 public List<UuidAndTitleCache<T>> getUuidAndTitleCache(){
277 Session session = getSession();
278 Query query = session.createQuery("select uuid, titleCache from " + type.getSimpleName());
279 return getUuidAndTitleCache(query);
280 }
281
282 protected <E extends IIdentifiableEntity> List<UuidAndTitleCache<E>> getUuidAndTitleCache(Query query){
283 List<UuidAndTitleCache<E>> list = new ArrayList<UuidAndTitleCache<E>>();
284
285 List<Object[]> result = query.list();
286
287 for(Object[] object : result){
288 list.add(new UuidAndTitleCache<E>((UUID) object[0], (String) object[1]));
289 }
290
291 return list;
292 }
293
294
295 public int countByTitle(Class<? extends T> clazz, String queryString, MatchMode matchmode, List<Criterion> criterion) {
296 return countByParam(clazz, "titleCache",queryString,matchmode,criterion);
297 }
298
299 public int countByReferenceTitle(Class<? extends T> clazz, String queryString, MatchMode matchmode, List<Criterion> criterion) {
300 return countByParam(clazz, "title",queryString,matchmode,criterion);
301 }
302
303 public int count(Class<? extends T> clazz, String queryString) {
304 checkNotInPriorView("IdentifiableDaoBase.count(Class<? extends T> clazz, String queryString)");
305 QueryParser queryParser = new QueryParser(version, defaultField , new StandardAnalyzer(version));
306
307 try {
308 org.apache.lucene.search.Query query = queryParser.parse(queryString);
309
310 FullTextSession fullTextSession = Search.getFullTextSession(this.getSession());
311 org.hibernate.search.FullTextQuery fullTextQuery = null;
312
313 if(clazz == null) {
314 fullTextQuery = fullTextSession.createFullTextQuery(query, type);
315 } else {
316 fullTextQuery = fullTextSession.createFullTextQuery(query, clazz);
317 }
318
319 Integer result = fullTextQuery.getResultSize();
320 return result;
321
322 } catch (ParseException e) {
323 throw new QueryParseException(e, queryString);
324 }
325 }
326
327 public void optimizeIndex() {
328 FullTextSession fullTextSession = Search.getFullTextSession(getSession());
329 SearchFactory searchFactory = fullTextSession.getSearchFactory();
330 for(Class clazz : indexedClasses) {
331 searchFactory.optimize(clazz); // optimize the indices ()
332 }
333 fullTextSession.flushToIndexes();
334 }
335
336 public void purgeIndex() {
337 FullTextSession fullTextSession = Search.getFullTextSession(getSession());
338 for(Class clazz : indexedClasses) {
339 fullTextSession.purgeAll(clazz); // remove all objects of type t from indexes
340 }
341 fullTextSession.flushToIndexes();
342 }
343
344 public void rebuildIndex() {
345 FullTextSession fullTextSession = Search.getFullTextSession(getSession());
346
347 for(T t : list(null,null)) { // re-index all objects
348 fullTextSession.index(t);
349 }
350 fullTextSession.flushToIndexes();
351 }
352
353 public List<T> search(Class<? extends T> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths) {
354 checkNotInPriorView("IdentifiableDaoBase.search(Class<? extends T> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths)");
355 QueryParser queryParser = new QueryParser(version, defaultField, new StandardAnalyzer(version));
356 List<T> results = new ArrayList<T>();
357
358 try {
359 org.apache.lucene.search.Query query = queryParser.parse(queryString);
360
361 FullTextSession fullTextSession = Search.getFullTextSession(getSession());
362 org.hibernate.search.FullTextQuery fullTextQuery = null;
363
364 if(clazz == null) {
365 fullTextQuery = fullTextSession.createFullTextQuery(query, type);
366 } else {
367 fullTextQuery = fullTextSession.createFullTextQuery(query, clazz);
368 }
369
370 addOrder(fullTextQuery,orderHints);
371
372 if(pageSize != null) {
373 fullTextQuery.setMaxResults(pageSize);
374 if(pageNumber != null) {
375 fullTextQuery.setFirstResult(pageNumber * pageSize);
376 } else {
377 fullTextQuery.setFirstResult(0);
378 }
379 }
380
381 List<T> result = (List<T>)fullTextQuery.list();
382 defaultBeanInitializer.initializeAll(result, propertyPaths);
383 return result;
384
385 } catch (ParseException e) {
386 throw new QueryParseException(e, queryString);
387 }
388 }
389
390 public String suggestQuery(String string) {
391 throw new UnsupportedOperationException("suggestQuery is not supported for objects of class " + type.getName());
392 }
393
394 @Override
395 public Integer countByTitle(String queryString) {
396 return countByTitle(queryString, null);
397 }
398
399 @Override
400 public Integer countByTitle(String queryString, CdmBase sessionObject) {
401 Session session = getSession();
402 if ( sessionObject != null ) {
403 session.update(sessionObject);
404 }
405 checkNotInPriorView("IdentifiableDaoBase.countByTitle(String queryString, CdmBase sessionObject)");
406 Criteria crit = session.createCriteria(type);
407 crit.add(Restrictions.ilike("titleCache", queryString));
408 Integer result = ((Number)crit.setProjection(Projections.rowCount()).uniqueResult()).intValue();
409 return result;
410 }
411
412 @Override
413 public Integer countByTitle(String queryString, MatchMode matchMode, List<Criterion> criteria) {
414 checkNotInPriorView("IdentifiableDaoBase.findByTitle(String queryString, MATCH_MODE matchmode, int page, int pagesize, List<Criterion> criteria)");
415 Criteria crit = getSession().createCriteria(type);
416 if (matchMode == MatchMode.EXACT) {
417 crit.add(Restrictions.eq("titleCache", matchMode.queryStringFrom(queryString)));
418 } else {
419 // crit.add(Restrictions.ilike("titleCache", matchmode.queryStringFrom(queryString)));
420 crit.add(Restrictions.like("titleCache", matchMode.queryStringFrom(queryString)));
421 }
422
423 if(criteria != null){
424 for (Criterion criterion : criteria) {
425 crit.add(criterion);
426 }
427 }
428
429
430 Integer result = ((Number)crit.setProjection(Projections.rowCount()).uniqueResult()).intValue();
431 return result;
432 }
433
434
435
436
437 }