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.
9 package eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.taxon
;
11 import java
.util
.ArrayList
;
12 import java
.util
.HashMap
;
13 import java
.util
.HashSet
;
14 import java
.util
.List
;
17 import java
.util
.UUID
;
19 import org
.apache
.logging
.log4j
.LogManager
;
20 import org
.apache
.logging
.log4j
.Logger
;
21 import org
.hibernate
.query
.Query
;
22 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
23 import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
24 import org
.springframework
.stereotype
.Repository
;
26 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
27 import eu
.etaxonomy
.cdm
.model
.common
.TreeIndex
;
28 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
29 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
30 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
31 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
32 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.common
.IdentifiableDaoBase
;
33 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
34 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
35 import eu
.etaxonomy
.cdm
.persistence
.dto
.ClassificationLookupDTO
;
36 import eu
.etaxonomy
.cdm
.persistence
.dto
.SortableTaxonNodeQueryResult
;
37 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
44 @Qualifier("classificationDaoHibernateImpl")
45 public class ClassificationDaoHibernateImpl
46 extends IdentifiableDaoBase
<Classification
>
47 implements IClassificationDao
{
48 @SuppressWarnings("unused")
49 private static final Logger logger
= LogManager
.getLogger();
52 private ITaxonNodeDao taxonNodeDao
;
54 public ClassificationDaoHibernateImpl() {
55 super(Classification
.class);
56 indexedClasses
= new Class
[1];
57 indexedClasses
[0] = Classification
.class;
61 public List
<TaxonNode
> listRankSpecificRootNodes(Classification classification
, TaxonNode taxonNode
, Rank rank
,
62 boolean includeUnpublished
, Integer limit
, Integer start
, List
<String
> propertyPaths
, int queryIndex
){
64 List
<TaxonNode
> results
= new ArrayList
<>();
65 Query
<TaxonNode
>[] queries
= prepareRankSpecificRootNodes(classification
, taxonNode
, rank
, includeUnpublished
, false, TaxonNode
.class);
67 // since this method is using two queries sequentially the handling of limit and start
68 // is a bit more complex
69 // the prepareRankSpecificRootNodes returns 1 or 2 queries
71 Query
<TaxonNode
> q
= queries
[queryIndex
];
73 q
.setMaxResults(limit
);
75 q
.setFirstResult(start
);
78 // long start_t = System.currentTimeMillis();
80 // System.err.println("dao.listRankSpecificRootNodes() - query[" + queryIndex + "].list() " + (System.currentTimeMillis() - start_t));
81 // start_t = System.currentTimeMillis();
82 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
83 // System.err.println("dao.listRankSpecificRootNodes() - defaultBeanInitializer.initializeAll() " + (System.currentTimeMillis() - start_t));
90 public long[] countRankSpecificRootNodes(Classification classification
, TaxonNode subtree
, boolean includeUnpublished
, Rank rank
) {
92 long[] result
= new long[(rank
== null ?
1 : 2)];
93 Query
<Long
>[] queries
= prepareRankSpecificRootNodes(classification
, subtree
, rank
, includeUnpublished
, true, Long
.class);
95 for(Query
<Long
> q
: queries
) {
96 result
[i
++] = q
.uniqueResult();
102 * See <a href="https://dev.e-taxonomy.eu/redmine/projects/edit/wiki/CdmClassificationRankSpecificRootnodes">
103 * https://dev.e-taxonomy.eu/redmine/projects/edit/wiki/CdmClassificationRankSpecificRootnodes</a>
105 * @param classification
108 * one or two Queries as array, depending on the <code>rank</code> parameter:
109 * <code>rank == null</code>: array with one item, <code>rank != null</code>: array with two items.
111 private <R
extends Object
> Query
<R
>[] prepareRankSpecificRootNodes(Classification classification
,
112 TaxonNode subtree
, Rank rank
,
113 boolean includeUnpublished
, boolean doCount
, Class
<R
> resultClass
) {
116 Query
<R
> query2
= null;
118 String whereClassification
= classification
!= null?
" AND tn.classification = :classification " : "";
119 String whereUnpublished
= includeUnpublished?
"" : " AND tn.taxon.publish = :publish ";
120 String whereSubtree
= subtree
!= null ?
" AND tn.treeIndex like :treeIndexLike " : "";
121 TreeIndex treeIndex
= TreeIndex
.NewInstance(subtree
);
122 String whereHighest
=
123 treeIndex
== null ?
" tn.parent.parent = null ":
124 treeIndex
.isTreeRoot() ?
" tn.parent.treeIndex = :treeIndex ":
125 " tn.treeIndex = :treeIndex " ;
127 String selectWhat
= doCount ?
"COUNT(distinct tn)" : "DISTINCT tn";
129 String joinFetch
= doCount ?
"" : " JOIN FETCH tn.taxon t JOIN FETCH t.name n LEFT JOIN FETCH n.rank LEFT JOIN FETCH t.secSource ss LEFT JOIN FETCH ss.citation ";
132 String hql
= "SELECT " + selectWhat
+
133 " FROM TaxonNode tn" +
135 " WHERE " + whereHighest
+
136 whereClassification
+ whereUnpublished
;
137 query1
= getSession().createQuery(hql
, resultClass
);
139 // this is for the cases
140 // - exact match of the ranks
141 // - rank of root node is lower but it has no parents
142 String hql1
= "SELECT " + selectWhat
+
143 " FROM TaxonNode tn " +
146 " (tn.taxon.name.rank = :rank" +
147 " OR ((tn.taxon.name.rank.orderIndex > :rankOrderIndex) AND (" + whereHighest
+ "))" +
149 + whereClassification
+ whereSubtree
+ whereUnpublished
;
151 // this is for the case
152 // - rank of root node is lower and it has a parent with higher rank
153 String whereParentSubtree
= subtree
!= null ?
" AND parent.treeIndex like :treeIndexLike " : "";
154 String hql2
= "SELECT " + selectWhat
+
155 " FROM TaxonNode tn JOIN tn.parent as parent" +
158 " (tn.taxon.name.rank.orderIndex > :rankOrderIndex "
159 + " AND parent.taxon.name.rank.orderIndex < :rankOrderIndex )"
160 + whereClassification
+ whereSubtree
161 + whereParentSubtree
+ whereUnpublished
;
163 query1
= getSession().createQuery(hql1
, resultClass
);
164 query2
= getSession().createQuery(hql2
, resultClass
);
165 query1
.setParameter("rank", rank
);
166 query1
.setParameter("rankOrderIndex", rank
.getOrderIndex());
167 query2
.setParameter("rankOrderIndex", rank
.getOrderIndex());
171 if (classification
!= null){
172 query1
.setParameter("classification", classification
);
174 query2
.setParameter("classification", classification
);
177 if (subtree
!= null){
178 query1
.setParameter("treeIndex", subtree
.treeIndex());
180 query1
.setParameter("treeIndexLike", subtree
.treeIndex()+"%");
183 query2
.setParameter("treeIndexLike", subtree
.treeIndex()+"%");
186 if (!includeUnpublished
){
187 query1
.setParameter("publish", true);
189 query2
.setParameter("publish", true);
194 return new Query
[]{query1
, query2
};
196 return new Query
[]{query1
};
201 public List
<TaxonNodeDto
> listChildrenOf(Taxon taxon
, Classification classification
, TaxonNode subtree
,
202 boolean includeUnpublished
, Integer pageSize
, Integer pageIndex
){
204 Query
<SortableTaxonNodeQueryResult
> query
= prepareListChildrenOf(taxon
, classification
,
205 subtree
, QueryType
.DTO
, includeUnpublished
, SortableTaxonNodeQueryResult
.class);
207 addPageSizeAndNumber(query
, pageSize
, pageIndex
);
209 List
<SortableTaxonNodeQueryResult
> queryResult
= query
.list();
210 //check if array is "empty" (not containing null objects) //copied from non-DTO version. Necessary here?
211 if(!queryResult
.isEmpty() && queryResult
.iterator().next()==null){
212 return java
.util
.Collections
.emptyList();
214 List
<TaxonNodeDto
> result
= SortableTaxonNodeQueryResult
.toTaxonNodeDtoList(queryResult
);
219 public List
<TaxonNode
> listChildrenOf(Taxon taxon
, Classification classification
, TaxonNode subtree
, boolean includeUnpublished
,
220 Integer pageSize
, Integer pageIndex
, List
<String
> propertyPaths
){
222 Query
<TaxonNode
> query
= prepareListChildrenOf(taxon
, classification
,
223 subtree
, QueryType
.INSTANCE
, includeUnpublished
, TaxonNode
.class);
225 addPageSizeAndNumber(query
, pageSize
, pageIndex
);
226 // query.setHint( "org.hibernate.readOnly", true ); //TODO does not seem to have an effect. (https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#hql-read-only-entities)
227 List
<TaxonNode
> result
= query
.list();
228 //check if array is "empty" (not containing null objects)
229 if(!result
.isEmpty() && result
.iterator().next()==null){
230 return java
.util
.Collections
.emptyList();
232 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
237 public TaxonNode
getRootNode(UUID classificationUuid
){
240 + " FROM TaxonNode tn, Classification c "
241 + " WHERE tn = c.rootNode AND c.uuid = :classificationUuid";
243 Query
<TaxonNode
> query
= getSession().createQuery(queryString
, TaxonNode
.class);
244 query
.setParameter("classificationUuid", classificationUuid
);
246 List
<TaxonNode
> results
= query
.list();
247 if(results
.size()!=1){
250 return taxonNodeDao
.load((results
.iterator().next()).getUuid());
254 public List
<TaxonNode
> listSiblingsOf(Taxon taxon
, Classification classification
, boolean includeUnpublished
,
255 Integer pageSize
, Integer pageIndex
, List
<String
> propertyPaths
){
256 Query
<TaxonNode
> query
= prepareListSiblingsOf(taxon
, classification
, includeUnpublished
, false, TaxonNode
.class);
258 addPageSizeAndNumber(query
, pageSize
, pageIndex
);
260 List
<TaxonNode
> result
= query
.list();
261 //check if array is "empty" (not containing null objects)
262 if(!result
.isEmpty() && result
.iterator().next()==null){
263 return java
.util
.Collections
.emptyList();
265 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
270 public Long
countChildrenOf(Taxon taxon
, Classification classification
, TaxonNode subtree
,
271 boolean includeUnpublished
){
272 Query
<Long
> query
= prepareListChildrenOf(taxon
, classification
, subtree
,
273 QueryType
.COUNT
, includeUnpublished
, Long
.class);
274 Long count
= query
.uniqueResult();
279 public Long
countSiblingsOf(Taxon taxon
, Classification classification
, boolean includeUnpublished
){
280 Query
<Long
> query
= prepareListSiblingsOf(taxon
, classification
, includeUnpublished
, true, Long
.class);
281 Long count
= query
.uniqueResult();
285 private enum QueryType
{COUNT
, DTO
, INSTANCE
}
287 public class TaxonNodeQueryResult
{
288 public UUID nodeUuid
; Integer nodeId
; UUID taxonUuid
; String taxonTitleCache
;
290 public TaxonNodeQueryResult(String titleCache
) {
292 public TaxonNodeQueryResult(UUID nodeUuid
) {
294 public TaxonNodeQueryResult(UUID nodeUuid
, int nodeId
, UUID taxonUuid
, String taxonTitleCache
) {
295 this.nodeUuid
= nodeUuid
;
296 this.nodeId
= nodeId
;
297 this.taxonUuid
= taxonUuid
;
298 this.taxonTitleCache
= taxonTitleCache
;
303 private <R
extends Object
> Query
<R
> prepareListChildrenOf(Taxon taxon
, Classification classification
, TaxonNode subtree
,
304 QueryType queryType
, boolean includeUnpublished
, Class
<R
> resultClass
){
306 String selectWhat
= queryType
== QueryType
.COUNT ?
"COUNT(cn)"
307 : queryType
== QueryType
.INSTANCE ?
"cn"
308 : (" new " +SortableTaxonNodeQueryResult
.class.getName()+"("
314 + ", ct.name.titleCache "
316 + ", tn.uuid " //parent.uuid
317 + ", index(cn) " //sortIndex,"
321 + ", cn.countChildren"
323 + ", ct.name.nameType"
324 + ", ct.name.genusOrUninomial"
325 + ", ct.name.infraGenericEpithet"
326 + ", ct.name.specificEpithet"
327 + ", ct.name.infraSpecificEpithet"
328 + ", ct.name.appendedPhrase"
329 + ", ct.name.protectedTitleCache"
330 + ", ct.name.protectedNameCache"
331 + ", ct.name.nameCache"
332 + ", ct.name.authorshipCache"
333 + ", ct.name.publicationYear"
334 + ", ct.name.monomHybrid"
335 + ", ct.name.binomHybrid"
336 + ", ct.name.trinomHybrid"
338 // + ", entry(cn.statusNote) " //cn.statusNote
339 // + ", cn.taxon.name.rank.orderIndex "
340 // + "cn.taxon.name.rank.titleCache " //TODO maybe ...rank.representations (?)
344 ; //TODO language dependent;
346 String hql
= "SELECT " + selectWhat
347 + " FROM TaxonNode AS tn "
348 + " JOIN tn.classification AS c "
349 + " JOIN tn.taxon AS t "
350 + " JOIN tn.childNodes AS cn "
351 + " JOIN cn.taxon AS ct "
352 + " JOIN ct.name AS ctn "
353 + " LEFT JOIN ctn.rank r "
354 + " LEFT JOIN ct.secSource ss "
355 + " LEFT JOIN ss.citation sec "
356 + " WHERE t = :taxon "
357 + " AND c = :classification";
358 if (!includeUnpublished
){
359 hql
+= " AND cn.taxon.publish = :publish ";
361 if (subtree
!= null){
362 hql
+= " AND tn.treeIndex like :treeIndexLike ";
364 Query
<R
> query
= getSession().createQuery(hql
, resultClass
);
365 query
.setParameter("taxon", taxon
);
366 query
.setParameter("classification", classification
);
367 if (!includeUnpublished
){
368 query
.setParameter("publish", Boolean
.TRUE
);
370 if (subtree
!= null){
371 query
.setParameter("treeIndexLike", subtree
.treeIndexLike());
376 private <R
extends Object
> Query
<R
> prepareListSiblingsOf(Taxon taxon
, Classification classification
,
377 boolean includeUnpublished
, boolean doCount
, Class
<R
> resultClass
){
379 String selectWhat
= doCount ?
"COUNT(tn)" : "tn";
380 String whereUnpublished
= includeUnpublished?
"" : " AND t.publish = :publish ";
384 + " FROM TaxonNode AS tn "
385 + " JOIN tn.classification AS c "
386 + " JOIN tn.taxon AS t "
387 + " WHERE t = :taxon "
388 + " AND c = :classification "
390 String hql
= " SELECT " + selectWhat
391 + " FROM TaxonNode as tn "
392 + " WHERE tn.parent IN ( " + subSelect
+ ")";
393 Query
<R
> query
= getSession().createQuery(hql
, resultClass
);
394 query
.setParameter("taxon", taxon
);
395 query
.setParameter("classification", classification
);
396 if (!includeUnpublished
){
397 query
.setParameter("publish", true);
404 public UUID
delete(Classification persistentObject
){
405 //delete all child nodes, then delete the tree
406 if (persistentObject
.getRootNode() != null){
407 List
<TaxonNode
> nodes
= persistentObject
.getChildNodes();
408 List
<TaxonNode
> nodesTmp
= new ArrayList
<>(nodes
);
409 for(TaxonNode node
: nodesTmp
){
410 persistentObject
.deleteChildNode(node
, true);
411 taxonNodeDao
.delete(node
, true);
415 TaxonNode rootNode
= persistentObject
.getRootNode();
416 persistentObject
.removeRootNode();
417 taxonNodeDao
.delete(rootNode
);
418 super.delete(persistentObject
);
420 return persistentObject
.getUuid();
424 public ClassificationLookupDTO
classificationLookup(Classification classification
) {
426 ClassificationLookupDTO classificationLookupDTO
= new ClassificationLookupDTO(classification
);
429 " SELECT t.id, n.rank, tp.id "
430 + " FROM TaxonNode AS tn "
431 + " JOIN tn.classification AS c "
432 + " JOIN tn.taxon AS t "
433 + " JOIN t.name AS n "
434 + " LEFT JOIN tn.parent AS tnp "
435 + " LEFT JOIN tnp.taxon as tp "
436 + " WHERE c = :classification";
438 Query
<Object
[]> query
= getSession().createQuery(hql
, Object
[].class);
439 query
.setParameter("classification", classification
);
441 List
<Object
[]> result
= query
.list();
442 for(Object
[] row
: result
) {
443 Integer parentId
= null;
444 parentId
= (Integer
) row
[2];
445 classificationLookupDTO
.add((Integer
)row
[0], (Rank
)row
[1], parentId
);
448 return classificationLookupDTO
;
452 public Map
<UUID
, TreeIndex
> treeIndexForTaxonUuids(UUID classificationUuid
,
453 List
<UUID
> taxonUuids
) {
454 String hql
= " SELECT t.uuid, tn.treeIndex "
455 + " FROM Taxon t JOIN t.taxonNodes tn "
457 + " AND tn.classification.uuid = :classificationUuid "
458 + " AND t.uuid IN (:taxonUuids) "
460 Query
<Object
[]> query
= getSession().createQuery(hql
, Object
[].class);
461 query
.setParameter("classificationUuid", classificationUuid
);
462 query
.setParameterList("taxonUuids", taxonUuids
);
464 Map
<UUID
, TreeIndex
> result
= new HashMap
<>();
465 List
<Object
[]> list
= query
.list();
466 for (Object
[] o
: list
){
467 result
.put((UUID
)o
[0], TreeIndex
.NewInstance((String
)o
[1]));
473 public Set
<TreeIndex
> getMarkedTreeIndexes(MarkerType markerType
, Boolean flag
){
474 String hql
= " SELECT tn.treeIndex "
476 + " JOIN t.taxonNodes tn "
477 + " JOIN t.markers m "
479 + " AND m.markerType = :markerType "
482 hql
+= " AND m.flag = :flag ";
486 Query
<String
> query
= getSession().createQuery(hql
, String
.class);
488 query
.setParameter("flag", flag
);
490 query
.setParameter("markerType", markerType
);
492 Set
<TreeIndex
> result
= new HashSet
<>();
494 List
<String
> list
= query
.list();
495 for (String o
: list
){
496 result
.add(TreeIndex
.NewInstance(o
));
502 public Map
<UUID
, UUID
> getTaxonNodeUuidByTaxonUuid(UUID classificationUuid
, List
<UUID
> taxonUuids
) {
503 String hql
= " SELECT t.uuid, tn.uuid "
504 + " FROM Taxon t JOIN t.taxonNodes tn "
506 + " AND tn.classification.uuid = :classificationUuid "
507 + " AND t.uuid IN (:taxonUuids) "
509 Query
<Object
[]> query
= getSession().createQuery(hql
, Object
[].class);
510 query
.setParameter("classificationUuid", classificationUuid
);
511 query
.setParameterList("taxonUuids", taxonUuids
);
513 Map
<UUID
, UUID
> result
= new HashMap
<>();
514 List
<Object
[]> list
= query
.list();
515 for (Object
[] o
: list
){
516 result
.put((UUID
)o
[0], (UUID
)o
[1]);