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
.util
.ArrayList
;
13 import java
.util
.List
;
14 import java
.util
.UUID
;
16 import org
.apache
.log4j
.Logger
;
17 import org
.apache
.lucene
.analysis
.standard
.StandardAnalyzer
;
18 import org
.apache
.lucene
.queryparser
.classic
.ParseException
;
19 import org
.apache
.lucene
.queryparser
.classic
.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
;
33 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
34 import eu
.etaxonomy
.cdm
.model
.common
.Credit
;
35 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
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
.MarkerType
;
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
;
47 public class IdentifiableDaoBase
<T
extends IdentifiableEntity
>
48 extends AnnotatableDaoImpl
<T
>
49 implements IIdentifiableDao
<T
>{
51 @SuppressWarnings("unused")
52 private static final Logger logger
= Logger
.getLogger(IdentifiableDaoBase
.class);
54 protected String defaultField
= "titleCache_tokenized";
55 protected Class
<?
extends T
> indexedClasses
[];
59 public IdentifiableDaoBase(Class
<T
> type
) {
65 public List
<T
> findByTitle(String queryString
) {
66 return findByTitle(queryString
, null);
70 public List
<T
> findByTitle(String queryString
, CdmBase sessionObject
) {
72 * FIXME why do we need to call update in a find* method? I don't know for sure
73 * that this is a good idea . . .
75 Session session
= getSession();
76 if ( sessionObject
!= null ) {
77 session
.update(sessionObject
);
79 checkNotInPriorView("IdentifiableDaoBase.findByTitle(String queryString, CdmBase sessionObject)");
80 Criteria crit
= session
.createCriteria(type
);
81 crit
.add(Restrictions
.ilike("titleCache", queryString
));
82 List
<T
> results
= crit
.list();
83 List
<String
> propertyPaths
= null;
84 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
89 public List
<T
> findByTitleAndClass(String queryString
, Class
<T
> clazz
) {
90 checkNotInPriorView("IdentifiableDaoBase.findByTitleAndClass(String queryString, Class<T> clazz)");
91 Criteria crit
= getSession().createCriteria(clazz
);
92 crit
.add(Restrictions
.ilike("titleCache", queryString
));
93 List
<T
> results
= crit
.list();
98 public List
<T
> findTitleCache(Class
<?
extends T
> clazz
, String queryString
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, MatchMode matchMode
){
100 Query query
= prepareFindTitleCache(clazz
, queryString
, pageSize
,
101 pageNumber
, matchMode
, false);
102 List
<T
> result
= query
.list();
107 public Long
countTitleCache(Class
<?
extends T
> clazz
, String queryString
, MatchMode matchMode
){
109 Query query
= prepareFindTitleCache(clazz
, queryString
, null,
110 null, matchMode
, true);
111 Long result
= (Long
)query
.uniqueResult();
116 * @param clazz filter by class - can be null to include all instances of type T
117 * @param queryString the query string to filter by
120 * @param matchmode use a particular type of matching (can be null - defaults to exact matching)
123 private Query
prepareFindTitleCache(Class
<?
extends T
> clazz
,
124 String queryString
, Integer pageSize
, Integer pageNumber
,
125 MatchMode matchMode
, boolean doCount
) {
130 String what
= (doCount ?
"count(distinct e.titleCache)": "distinct e.titleCache");
132 if(matchMode
!= null){
133 queryString
= matchMode
.queryStringFrom(queryString
);
135 String hql
= "select " + what
+ " from " + clazz
.getName() + " e where e.titleCache like '" + queryString
+ "'";
137 Query query
= getSession().createQuery(hql
);
139 if(pageSize
!= null && !doCount
) {
140 query
.setMaxResults(pageSize
);
141 if(pageNumber
!= null) {
142 query
.setFirstResult(pageNumber
* pageSize
);
150 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
) {
151 return findByParam(clazz
, "titleCache", queryString
, matchmode
, criterion
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
155 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
) {
156 return findByParam(clazz
, "title", queryString
, matchmode
, criterion
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
160 public List
<T
> findByTitle(String queryString
, MatchMode matchmode
, int page
, int pagesize
, List
<Criterion
> criteria
) {
161 checkNotInPriorView("IdentifiableDaoBase.findByTitle(String queryString, MATCH_MODE matchmode, int page, int pagesize, List<Criterion> criteria)");
162 Criteria crit
= getSession().createCriteria(type
);
163 if (matchmode
== MatchMode
.EXACT
) {
164 crit
.add(Restrictions
.eq("titleCache", matchmode
.queryStringFrom(queryString
)));
166 // crit.add(Restrictions.ilike("titleCache", matchmode.queryStringFrom(queryString)));
167 crit
.add(Restrictions
.like("titleCache", matchmode
.queryStringFrom(queryString
)));
170 crit
.setMaxResults(pagesize
);
172 if(criteria
!= null){
173 for (Criterion criterion
: criteria
) {
177 crit
.addOrder(Order
.asc("titleCache"));
178 int firstItem
= (page
- 1) * pagesize
;
179 crit
.setFirstResult(firstItem
);
180 List
<T
> results
= crit
.list();
181 List
<String
> propertyPaths
= null;
182 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
187 public int countRights(T identifiableEntity
) {
188 checkNotInPriorView("IdentifiableDaoBase.countRights(T identifiableEntity)");
189 Query query
= getSession().createQuery("select count(rights) from " + type
.getSimpleName() + " identifiableEntity join identifiableEntity.rights rights where identifiableEntity = :identifiableEntity");
190 query
.setParameter("identifiableEntity",identifiableEntity
);
191 return ((Long
)query
.uniqueResult()).intValue();
195 public int countSources(T identifiableEntity
) {
196 checkNotInPriorView("IdentifiableDaoBase.countSources(T identifiableEntity)");
197 Query query
= getSession().createQuery("SELECT COUNT(source) FROM "+identifiableEntity
.getClass().getName() + " ie JOIN ie.sources source WHERE ie = :identifiableEntity");
198 query
.setParameter("identifiableEntity", identifiableEntity
);
199 return ((Long
)query
.uniqueResult()).intValue();
203 public List
<Rights
> getRights(T identifiableEntity
, Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
204 checkNotInPriorView("IdentifiableDaoBase.getRights(T identifiableEntity, Integer pageSize, Integer pageNumber, List<String> propertyPaths)");
205 Query query
= getSession().createQuery("select rights from " + type
.getSimpleName() + " identifiableEntity join identifiableEntity.rights rights where identifiableEntity = :identifiableEntity");
206 query
.setParameter("identifiableEntity",identifiableEntity
);
207 setPagingParameter(query
, pageSize
, pageNumber
);
208 List
<Rights
> results
= query
.list();
209 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
213 // @Override //TODO add to interface, maybe add property path
214 public List
<Credit
> getCredits(T identifiableEntity
, Integer pageSize
, Integer pageNumber
) {
215 checkNotInPriorView("IdentifiableDaoBase.getCredits(T identifiableEntity, Integer pageSize, Integer pageNumber)");
216 Query query
= getSession().createQuery("select credits from " + type
.getSimpleName() + " identifiableEntity join identifiableEntity.credits credits where identifiableEntity = :identifiableEntity");
217 query
.setParameter("identifiableEntity",identifiableEntity
);
218 setPagingParameter(query
, pageSize
, pageNumber
);
223 public List
<IdentifiableSource
> getSources(T identifiableEntity
, Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
224 checkNotInPriorView("IdentifiableDaoBase.getSources(T identifiableEntity, Integer pageSize, Integer pageNumber)");
225 Query query
= getSession().createQuery("SELECT source FROM "+ identifiableEntity
.getClass().getName()+ " ie JOIN ie.sources source WHERE ie.id = :id");
226 query
.setParameter("id",identifiableEntity
.getId());
227 setPagingParameter(query
, pageSize
, pageNumber
);
228 @SuppressWarnings("unchecked")
229 List
<IdentifiableSource
> results
= query
.list();
230 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
235 public List
<T
> findOriginalSourceByIdInSource(String idInSource
, String idNamespace
) {
236 checkNotInPriorView("IdentifiableDaoBase.findOriginalSourceByIdInSource(String idInSource, String idNamespace)");
237 Query query
= getSession().createQuery(
238 "Select c from " + type
.getSimpleName() + " as c " +
239 "inner join c.sources as source " +
240 "where source.idInSource = :idInSource " +
241 " AND source.idNamespace = :idNamespace"
243 query
.setString("idInSource", idInSource
);
244 query
.setString("idNamespace", idNamespace
);
245 //TODO integrate reference in where
250 public T
find(LSID lsid
) {
251 checkNotInPriorView("IdentifiableDaoBase.find(LSID lsid)");
252 Criteria criteria
= getSession().createCriteria(type
);
253 criteria
.add(Restrictions
.eq("lsid.authority", lsid
.getAuthority()));
254 criteria
.add(Restrictions
.eq("lsid.namespace", lsid
.getNamespace()));
255 criteria
.add(Restrictions
.eq("lsid.object", lsid
.getObject()));
257 if(lsid
.getRevision() != null) {
258 criteria
.add(Restrictions
.eq("lsid.revision", lsid
.getRevision()));
261 T object
= (T
)criteria
.uniqueResult();
265 AuditQuery query
= getAuditReader().createQuery().forRevisionsOfEntity(type
, false, true);
266 query
.add(AuditEntity
.property("lsid_authority").eq(lsid
.getAuthority()));
267 query
.add(AuditEntity
.property("lsid_namespace").eq(lsid
.getNamespace()));
268 query
.add(AuditEntity
.property("lsid_object").eq(lsid
.getObject()));
270 if(lsid
.getRevision() != null) {
271 query
.add(AuditEntity
.property("lsid_revision").eq(lsid
.getRevision()));
274 query
.addOrder(AuditEntity
.revisionNumber().asc());
275 query
.setMaxResults(1);
276 query
.setFirstResult(0);
277 List
<Object
[]> objs
= query
.getResultList();
281 return (T
)objs
.get(0)[0];
288 @SuppressWarnings("deprecation")
290 public String
getTitleCache(UUID uuid
, boolean refresh
){
292 String queryStr
= String
.format("SELECT titleCache FROM %s t WHERE t.uuid = '%s'", type
.getSimpleName() , uuid
.toString());
293 Query query
= getSession().createQuery(queryStr
);
294 List
<?
> list
= query
.list();
295 return list
.isEmpty()?
null : (String
)list
.get(0);
297 T entity
= this.findByUuid(uuid
);
301 entity
.setTitleCache(null);
302 return entity
.getTitleCache();
309 public long countByTitle(Class
<?
extends T
> clazz
, String queryString
, MatchMode matchmode
, List
<Criterion
> criterion
) {
310 return countByParam(clazz
, "titleCache",queryString
,matchmode
,criterion
);
314 public long countByReferenceTitle(Class
<?
extends T
> clazz
, String queryString
, MatchMode matchmode
, List
<Criterion
> criterion
) {
315 return countByParam(clazz
, "title",queryString
,matchmode
,criterion
);
319 public int count(Class
<?
extends T
> clazz
, String queryString
) {
320 checkNotInPriorView("IdentifiableDaoBase.count(Class<? extends T> clazz, String queryString)");
321 QueryParser queryParser
= new QueryParser(defaultField
, new StandardAnalyzer());
324 org
.apache
.lucene
.search
.Query query
= queryParser
.parse(queryString
);
326 FullTextSession fullTextSession
= Search
.getFullTextSession(this.getSession());
327 org
.hibernate
.search
.FullTextQuery fullTextQuery
= null;
330 fullTextQuery
= fullTextSession
.createFullTextQuery(query
, type
);
332 fullTextQuery
= fullTextSession
.createFullTextQuery(query
, clazz
);
335 Integer result
= fullTextQuery
.getResultSize();
338 } catch (ParseException e
) {
339 throw new QueryParseException(e
, queryString
);
344 public void optimizeIndex() {
345 FullTextSession fullTextSession
= Search
.getFullTextSession(getSession());
346 SearchFactory searchFactory
= fullTextSession
.getSearchFactory();
347 for(Class clazz
: indexedClasses
) {
348 searchFactory
.optimize(clazz
); // optimize the indices ()
350 fullTextSession
.flushToIndexes();
354 public void purgeIndex() {
355 FullTextSession fullTextSession
= Search
.getFullTextSession(getSession());
356 for(Class clazz
: indexedClasses
) {
357 fullTextSession
.purgeAll(clazz
); // remove all objects of type t from indexes
359 fullTextSession
.flushToIndexes();
363 public void rebuildIndex() {
364 FullTextSession fullTextSession
= Search
.getFullTextSession(getSession());
366 for(T t
: list(null,null)) { // re-index all objects
367 fullTextSession
.index(t
);
369 fullTextSession
.flushToIndexes();
373 public List
<T
> search(Class
<?
extends T
> clazz
, String queryString
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
,List
<String
> propertyPaths
) {
374 checkNotInPriorView("IdentifiableDaoBase.search(Class<? extends T> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,List<String> propertyPaths)");
375 QueryParser queryParser
= new QueryParser(defaultField
, new StandardAnalyzer());
376 List
<T
> results
= new ArrayList
<T
>();
379 org
.apache
.lucene
.search
.Query query
= queryParser
.parse(queryString
);
381 FullTextSession fullTextSession
= Search
.getFullTextSession(getSession());
382 org
.hibernate
.search
.FullTextQuery fullTextQuery
= null;
385 fullTextQuery
= fullTextSession
.createFullTextQuery(query
, type
);
387 fullTextQuery
= fullTextSession
.createFullTextQuery(query
, clazz
);
390 addOrder(fullTextQuery
,orderHints
);
392 if(pageSize
!= null) {
393 fullTextQuery
.setMaxResults(pageSize
);
394 if(pageNumber
!= null) {
395 fullTextQuery
.setFirstResult(pageNumber
* pageSize
);
397 fullTextQuery
.setFirstResult(0);
401 List
<T
> result
= fullTextQuery
.list();
402 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
405 } catch (ParseException e
) {
406 throw new QueryParseException(e
, queryString
);
411 public String
suggestQuery(String string
) {
412 throw new UnsupportedOperationException("suggestQuery is not supported for objects of class " + type
.getName());
416 public Integer
countByTitle(String queryString
) {
417 return countByTitle(queryString
, null);
421 public Integer
countByTitle(String queryString
, CdmBase sessionObject
) {
422 Session session
= getSession();
423 if ( sessionObject
!= null ) {
424 session
.update(sessionObject
);
426 checkNotInPriorView("IdentifiableDaoBase.countByTitle(String queryString, CdmBase sessionObject)");
427 Criteria crit
= session
.createCriteria(type
);
428 crit
.add(Restrictions
.ilike("titleCache", queryString
));
429 Integer result
= ((Number
)crit
.setProjection(Projections
.rowCount()).uniqueResult()).intValue();
434 public Integer
countByTitle(String queryString
, MatchMode matchMode
, List
<Criterion
> criteria
) {
435 checkNotInPriorView("IdentifiableDaoBase.findByTitle(String queryString, MATCH_MODE matchmode, int page, int pagesize, List<Criterion> criteria)");
436 Criteria crit
= getSession().createCriteria(type
);
437 if (matchMode
== MatchMode
.EXACT
) {
438 crit
.add(Restrictions
.eq("titleCache", matchMode
.queryStringFrom(queryString
)));
440 // crit.add(Restrictions.ilike("titleCache", matchmode.queryStringFrom(queryString)));
441 crit
.add(Restrictions
.like("titleCache", matchMode
.queryStringFrom(queryString
)));
444 if(criteria
!= null){
445 for (Criterion criterion
: criteria
) {
451 Integer result
= ((Number
)crit
.setProjection(Projections
.rowCount()).uniqueResult()).intValue();
457 public <S
extends T
> int countByIdentifier(Class
<S
> clazz
,
458 String identifier
, DefinedTerm identifierType
, MatchMode matchmode
) {
459 checkNotInPriorView("IdentifiableDaoBase.countByIdentifier(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode)");
461 Class
<?
> clazzParam
= clazz
== null ? type
: clazz
;
462 String queryString
= "SELECT count(*) FROM " + clazzParam
.getSimpleName() + " as c " +
463 "INNER JOIN c.identifiers as ids " +
465 if (identifier
!= null){
466 if (matchmode
== null || matchmode
== MatchMode
.EXACT
){
467 queryString
+= " AND ids.identifier = '" + identifier
+ "'";
469 queryString
+= " AND ids.identifier LIKE '" + matchmode
.queryStringFrom(identifier
) + "'";
472 if (identifierType
!= null){
473 queryString
+= " AND ids.type = :type";
476 Query query
= getSession().createQuery(queryString
);
477 if (identifierType
!= null){
478 query
.setEntity("type", identifierType
);
481 Long c
= (Long
)query
.uniqueResult();
486 public <S
extends T
> List
<Object
[]> findByIdentifier(
487 Class
<S
> clazz
, String identifier
, DefinedTerm identifierType
,
488 MatchMode matchmode
, boolean includeEntity
,
489 Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
491 checkNotInPriorView("IdentifiableDaoBase.findByIdentifier(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
493 Class
<?
> clazzParam
= clazz
== null ? type
: clazz
;
494 String queryString
= "SELECT ids.type, ids.identifier, %s FROM %s as c " +
495 " INNER JOIN c.identifiers as ids " +
497 queryString
= String
.format(queryString
, (includeEntity ?
"c":"c.uuid, c.titleCache") , clazzParam
.getSimpleName());
499 //Matchmode and identifier
500 if (identifier
!= null){
501 if (matchmode
== null || matchmode
== MatchMode
.EXACT
){
502 queryString
+= " AND ids.identifier = '" + identifier
+ "'";
504 queryString
+= " AND ids.identifier LIKE '" + matchmode
.queryStringFrom(identifier
) + "'";
507 if (identifierType
!= null){
508 queryString
+= " AND ids.type = :type";
511 queryString
+=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
513 Query query
= getSession().createQuery(queryString
);
516 if (identifierType
!= null){
517 query
.setEntity("type", identifierType
);
521 setPagingParameter(query
, pageSize
, pageNumber
);
523 List
<Object
[]> results
= query
.list();
526 List
<S
> entities
= new ArrayList
<S
>();
527 for (Object
[] result
: results
){
528 entities
.add((S
)result
[2]);
530 defaultBeanInitializer
.initializeAll(entities
, propertyPaths
);
536 public <S
extends T
> long countByMarker(Class
<S
> clazz
, MarkerType markerType
,
537 Boolean markerValue
) {
538 checkNotInPriorView("IdentifiableDaoBase.countByMarker(T clazz, MarkerType markerType, Boolean markerValue)");
540 if (markerType
== null){
543 Class
<?
> clazzParam
= clazz
== null ? type
: clazz
;
544 String queryString
= "SELECT count(*) FROM " + clazzParam
.getSimpleName() + " as c " +
545 "INNER JOIN c.markers as mks " +
548 if (markerValue
!= null){
549 queryString
+= " AND mks.flag = :flag";
551 queryString
+= " AND mks.markerType = :type";
553 Query query
= getSession().createQuery(queryString
);
554 query
.setEntity("type", markerType
);
555 if (markerValue
!= null){
556 query
.setBoolean("flag", markerValue
);
559 Long c
= (Long
)query
.uniqueResult();
564 public <S
extends T
> List
<Object
[]> findByMarker(
565 Class
<S
> clazz
, MarkerType markerType
,
566 Boolean markerValue
, boolean includeEntity
, Integer pageSize
, Integer pageNumber
,
567 List
<String
> propertyPaths
) {
569 checkNotInPriorView("IdentifiableDaoBase.findByMarker(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
570 if (markerType
== null){
571 return new ArrayList
<Object
[]>();
574 Class
<?
> clazzParam
= clazz
== null ? type
: clazz
;
575 String queryString
= "SELECT mks.markerType, mks.flag, %s FROM %s as c " +
576 " INNER JOIN c.markers as mks " +
578 queryString
= String
.format(queryString
, (includeEntity ?
"c":"c.uuid, c.titleCache") , clazzParam
.getSimpleName());
580 //Matchmode and identifier
581 if (markerValue
!= null){
582 queryString
+= " AND mks.flag = :flag";
584 queryString
+= " AND mks.markerType = :type";
587 queryString
+=" ORDER BY mks.markerType.uuid, mks.flag, c.uuid ";
589 Query query
= getSession().createQuery(queryString
);
592 query
.setEntity("type", markerType
);
593 if (markerValue
!= null){
594 query
.setBoolean("flag", markerValue
);
598 setPagingParameter(query
, pageSize
, pageNumber
);
600 @SuppressWarnings("unchecked")
601 List
<Object
[]> results
= query
.list();
604 List
<S
> entities
= new ArrayList
<S
>();
605 for (Object
[] result
: results
){
606 entities
.add((S
)result
[2]);
608 defaultBeanInitializer
.initializeAll(entities
, propertyPaths
);