Project

General

Profile

« Previous | Next » 

Revision f4d35a72

Added by Andreas Kohlbecker almost 8 years ago

attempt to reduce the overhead imposed by database access

View differences:

cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/hibernate/taxon/ClassificationDaoHibernateImpl.java
27 27
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
28 28
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
29 29
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
30
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
30 31

  
31 32
/**
32 33
 * @author a.mueller
......
254 255
        return persistentObject.getUuid();
255 256
    }
256 257

  
258
    @Override
259
    public ClassificationLookupDTO classificationLookup(Classification classification) {
260

  
261
        ClassificationLookupDTO classificationLookupDTO = new ClassificationLookupDTO(classification);
262

  
263
        // only for debugging:
264
//        logger.setLevel(Level.TRACE);
265
//        Logger.getLogger("org.hibernate.SQL").setLevel(Level.DEBUG);
266

  
267
        String hql = "select t.id, n.rank, tp.id from TaxonNode as tn join tn.classification as c join tn.taxon as t join t.name as n "
268
                + " left join tn.parent as tnp left join tnp.taxon as tp "
269
                + " where c = :classification";
270
        Query query = getSession().createQuery(hql);
271
        query.setParameter("classification", classification);
272
        @SuppressWarnings("unchecked")
273
        List<Object[]> result = query.list();
274
        for(Object[] row : result) {
275
            Integer parentId = null;
276
//            if(row.length == 3) { // TODO check necessary?
277
//                parentId = (Integer) row[2];
278
//            }
279
            classificationLookupDTO.add((Integer)row[0], (Rank)row[1], parentId);
280
        }
281

  
282
        return classificationLookupDTO ;
283
    }
284

  
257 285

  
258 286

  
259 287

  
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dao/taxon/IClassificationDao.java
16 16
import eu.etaxonomy.cdm.model.taxon.Taxon;
17 17
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
18 18
import eu.etaxonomy.cdm.persistence.dao.common.IIdentifiableDao;
19
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
19 20

  
20 21
/**
21 22
 * @author a.mueller
......
63 64
    public List<TaxonNode> listChildrenOf(Taxon taxon, Classification classification, Integer pageSize, Integer pageIndex, List<String> propertyPaths);
64 65

  
65 66

  
66
    public abstract Long countChildrenOf(Taxon taxon, Classification classification);
67
    public Long countChildrenOf(Taxon taxon, Classification classification);
68

  
69
    public ClassificationLookupDTO classificationLookup(Classification classification);
67 70

  
68 71
    /**
69 72
     * @param taxon
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/dto/ClassificationLookupDTO.java
1
// $Id$
2
/**
3
* Copyright (C) 2015 EDIT
4
* European Distributed Institute of Taxonomy
5
* http://www.e-taxonomy.eu
6
*
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
*/
10
package eu.etaxonomy.cdm.persistence.dto;
11

  
12
import java.util.Collection;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.Map;
16
import java.util.Set;
17

  
18
import eu.etaxonomy.cdm.model.name.Rank;
19
import eu.etaxonomy.cdm.model.taxon.Classification;
20

  
21
/**
22
 * @author a.kohlbecker
23
 * @date Sep 3, 2015
24
 *
25
 */
26
public class ClassificationLookupDTO {
27

  
28
    private final Map<Integer, Integer> taxonIdToParentId = new HashMap<Integer, Integer>();
29
    private final Map<Rank,Collection<Integer>> taxonIdByRank = new HashMap<Rank, Collection<Integer>>();
30
    private final Map<Integer,Collection<Integer>> childTaxonMap = new HashMap<Integer,Collection<Integer>>();
31
    private Classification classification = null;
32

  
33
    /**
34
     * @return the taxonIds
35
     */
36
    public Set<Integer> getTaxonIds() {
37
        return taxonIdToParentId.keySet();
38
    }
39

  
40
    /**
41
     * @return the taxonIdByRank
42
     */
43
    public Map<Rank, Collection<Integer>> getTaxonIdByRank() {
44
        return taxonIdByRank;
45
    }
46

  
47
    /**
48
     * @return the childTaxonMap
49
     */
50
    public Map<Integer, Collection<Integer>> getChildTaxonMap() {
51
        return childTaxonMap;
52
    }
53

  
54
    /**
55
     * @return the classification
56
     */
57
    public Classification getClassification() {
58
        return classification;
59
    }
60

  
61
    /**
62
     *
63
     * @param classification
64
     *      Must never be null the ClassificationLookupDTO always specific to one
65
     *      Classification.
66
     */
67
    public ClassificationLookupDTO(Classification classification) {
68
        this.classification  = classification;
69
    }
70

  
71
    public void add(Integer taxonId, Rank rank, Integer parentId) {
72

  
73
        taxonIdToParentId.put(taxonId, parentId);
74

  
75
        if(!childTaxonMap.containsKey(parentId)) {
76
            childTaxonMap.put(parentId, new HashSet<Integer>());
77
        }
78
        childTaxonMap.get(parentId).add(taxonId);
79

  
80
        if(!taxonIdByRank.containsKey(rank)) {
81
            taxonIdByRank.put(rank, new HashSet<Integer>());
82
        }
83
        taxonIdByRank.get(rank).add(taxonId);
84
    }
85

  
86
    public void dropRank(Rank rank) {
87
        Collection<Integer> idsForRank = taxonIdByRank.get(rank);
88
        taxonIdByRank.remove(rank);
89

  
90
        for(Integer taxonId : idsForRank) {
91
            Integer parentId = taxonIdToParentId.get(taxonId);
92
            taxonIdToParentId.remove(taxonId);
93
            childTaxonMap.remove(parentId);
94
        }
95
    }
96

  
97
}
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/persistence/dao/hibernate/taxon/ClassificationDaoHibernateImplTest.java
30 30
import eu.etaxonomy.cdm.persistence.dao.reference.IReferenceDao;
31 31
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
32 32
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
33
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
33 34
import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
34 35
import eu.etaxonomy.cdm.test.unitils.CleanSweepInsertLoadStrategy;
35 36

  
......
166 167
//        }
167 168
	}
168 169

  
170
    @Test
171
    @DataSet(value="ClassificationDaoHibernateImplTest.listRankSpecificRootNodes.xml")
172
    public void testClassificationLookup() {
173

  
174
        Classification classification = classificationDao.load(UUID.fromString(CLASSIFICATION_FULL_UUID));
175
        ClassificationLookupDTO classificationLookupDto = classificationDao.classificationLookup(classification);
176
        assertEquals(4, classificationLookupDto.getTaxonIds().size());
177
    }
178

  
169 179

  
170 180
    /**
171 181
     * At the moment the data created is special to the issue http://dev.e-taxonomy.eu/trac/ticket/2778
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/IService.java
161 161
     *
162 162
     * @param idSet
163 163
     * @return
164
     * @deprecated use {@link #listByIds(Set, Integer, Integer, List, List)} instead
164 165
     */
166
    @Deprecated
165 167
    public List<T> findById(Set<Integer> idSet);  //can't be called find(Set<Integer>) as this conflicts with find(Set<UUID)
166 168

  
167 169

  
......
377 379
     */
378 380
    public List<T> merge(List<T> detachedObjects);
379 381

  
382
    /**
383
     * @param idSet
384
     * @param pageSize
385
     * @param pageNumber
386
     * @param orderHints
387
     * @param propertyPaths
388
     * @return
389
     */
390
    List<T> listByIds(Set<Integer> idSet, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
391
            List<String> propertyPaths);
392

  
380 393
    /**
381 394
     * This method allows for the possibility of returning the input transient
382 395
     * entities instead of the merged persistent entity
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/ServiceBase.java
120 120
        return dao.listByIds(idSet, null, null, null, null);
121 121
    }
122 122

  
123
    @Override
124
    @Transactional(readOnly = true)
125
    public List<T> listByIds(Set<Integer> idSet, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
126
        return dao.listByIds(idSet, pageSize, pageNumber, orderHints, propertyPaths);
127
    }
128

  
123 129
    @Override
124 130
    @Transactional(readOnly = true)
125 131
    public T find(UUID uuid) {
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/description/TransmissionEngineDistribution.java
13 13
import java.util.Arrays;
14 14
import java.util.HashMap;
15 15
import java.util.HashSet;
16
import java.util.Iterator;
16 17
import java.util.List;
17 18
import java.util.Map;
18 19
import java.util.Set;
......
46 47
import eu.etaxonomy.cdm.model.common.ExtensionType;
47 48
import eu.etaxonomy.cdm.model.common.Marker;
48 49
import eu.etaxonomy.cdm.model.common.MarkerType;
50
import eu.etaxonomy.cdm.model.common.OrderedTermBase;
49 51
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
50 52
import eu.etaxonomy.cdm.model.description.Distribution;
51 53
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
......
54 56
import eu.etaxonomy.cdm.model.name.Rank;
55 57
import eu.etaxonomy.cdm.model.taxon.Classification;
56 58
import eu.etaxonomy.cdm.model.taxon.Taxon;
59
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
57 60
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
61
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
62
import eu.etaxonomy.cdm.persistence.dto.ClassificationLookupDTO;
58 63

  
59 64
/**
60 65
 * The TransmissionEngineDistribution is meant to be used from within a service class.
......
100 105
    final boolean ONLY_FISRT_BATCH = false;
101 106

  
102 107

  
108
    protected static final List<String> TAXONDESCRIPTION_INIT_STRATEGY = Arrays.asList(new String [] {
109
            "description.markers.markerType",
110
            "description.elements.markers.markerType",
111
            "description.elements.area",
112
            "description.elements.sources.citation.authorship",
113
            "description.elements.sources.nameUsedInSource",
114
            "description.elements.multilanguageText",
115
            "name.status.type",
116
    });
117

  
118

  
103 119
    /**
104 120
     * A map which contains the status terms as key and the priority as value
105 121
     * The map will contain both, the PresenceTerms and the AbsenceTerms
......
118 134
    @Autowired
119 135
    private IClassificationService classificationService;
120 136

  
137
    @Autowired
138
    private IClassificationDao classificationDao;
139

  
121 140
    @Autowired
122 141
    private INameService mameService;
123 142

  
......
311 330
            monitor = new NullProgressMonitor();
312 331
        }
313 332

  
314
        // take start time for performance testing
315
        // NOTE: use ONLY_FISRT_BATCH = true to measure only one batch
316
        double start = System.currentTimeMillis();
333
        logger.setLevel(Level.INFO); // TRACE will slow down a lot since it forces loading all term representations
334

  
335
        logger.info("Hibernate JDBC Batch size: "
336
                + ((SessionFactoryImplementor) getSession().getSessionFactory()).getSettings().getJdbcBatchSize());
317 337

  
318 338
        // only for debugging:
319 339
        logger.setLevel(Level.INFO);
320 340
        //Logger.getLogger("org.hibernate.SQL").setLevel(Level.DEBUG);
321 341

  
322
        logger.info("Hibernate JDBC Batch size: "
323
                + ((SessionFactoryImplementor) getSession().getSessionFactory()).getSettings().getJdbcBatchSize());
342
        Set<Classification> classifications = new HashSet<Classification>();
343
        if(classification == null) {
344
            classifications.addAll(classificationService.listClassifications(null, null, null, null));
345
        } else {
346
            classifications.add(classification);
347
        }
324 348

  
325
        int workTicks = mode.equals(AggregationMode.byAreasAndRanks) ? 400 : 200;
326
        monitor.beginTask("Accumulating distributions", workTicks + 1 );
349
        int aggregationWorkTicks = mode.equals(AggregationMode.byAreasAndRanks) ? 400 : 200;
327 350

  
351
        // take start time for performance testing
352
        // NOTE: use ONLY_FISRT_BATCH = true to measure only one batch
353
        double start = System.currentTimeMillis();
328 354

  
329
        monitor.subTask("updating Priorities");
355
        monitor.beginTask("Accumulating distributions", (classifications.size() * aggregationWorkTicks) + 1 );
330 356
        updatePriorities();
331 357
        monitor.worked(1);
332
        monitor.setTaskName("Accumulating distributions");
333 358

  
334
        monitor.subTask("Accumulating distributions to super areas");
335
        if (mode.equals(AggregationMode.byAreas) || mode.equals(AggregationMode.byAreasAndRanks)) {
336
            accumulateByArea(superAreas, classification, new SubProgressMonitor(monitor, 200),
337
                    mode.equals(AggregationMode.byAreas) || mode.equals(AggregationMode.byAreasAndRanks));
338
        }
339
        double end1 = System.currentTimeMillis();
340
        logger.info("Time elapsed for accumulateByArea() : " + (end1 - start) / (1000) + "s");
341

  
342
        double start2 = System.currentTimeMillis();
343
        monitor.subTask("Accumulating distributions to higher ranks");
344
        if (mode.equals(AggregationMode.byRanks) || mode.equals(AggregationMode.byAreasAndRanks)) {
345
            accumulateByRank(lowerRank, upperRank, classification, new SubProgressMonitor(monitor, 200),
346
                    mode.equals(AggregationMode.byRanks));
347
        }
359
        for(Classification _classification : classifications) {
360

  
361
            ClassificationLookupDTO classificationLookupDao = classificationDao.classificationLookup(_classification);
362

  
363
            monitor.subTask("Accumulating distributions to super areas for " + _classification.getTitleCache());
364
            if (mode.equals(AggregationMode.byAreas) || mode.equals(AggregationMode.byAreasAndRanks)) {
365
                accumulateByArea(superAreas, classificationLookupDao, new SubProgressMonitor(monitor, 200),
366
                        mode.equals(AggregationMode.byAreas) || mode.equals(AggregationMode.byAreasAndRanks));
367
            }
368
            monitor.subTask("Accumulating distributions to higher ranks for " + _classification.getTitleCache());
369

  
370
            double end1 = System.currentTimeMillis();
348 371

  
349
        double end2 = System.currentTimeMillis();
350
        logger.info("Time elapsed for accumulateByRank() : " + (end2 - start2) / (1000) + "s");
351
        logger.info("Time elapsed for accumulate(): " + (end2 - start) / (1000) + "s");
372
            logger.info("Time elapsed for accumulateByArea() : " + (end1 - start) / (1000) + "s");
373

  
374
            double start2 = System.currentTimeMillis();
375
            if (mode.equals(AggregationMode.byRanks) || mode.equals(AggregationMode.byAreasAndRanks)) {
376
                accumulateByRank(lowerRank, upperRank, classification, new SubProgressMonitor(monitor, 200),
377
                        mode.equals(AggregationMode.byRanks));
378
            }
379

  
380
            double end2 = System.currentTimeMillis();
381
            logger.info("Time elapsed for accumulateByRank() : " + (end2 - start2) / (1000) + "s");
382
            logger.info("Time elapsed for accumulate(): " + (end2 - start) / (1000) + "s");
383

  
384
            if(ONLY_FISRT_BATCH) {
385
                break;
386
            }
387
        }
352 388
    }
353 389

  
354 390
    /**
......
371 407
     *
372 408
     * @param superAreas
373 409
     *      the areas to which the subordinate areas should be projected
374
     * @param classification
375
     *      limit the accumulation process to a specific classification (not yet implemented)
410
     * @param classificationLookupDao
411
     *
376 412
     */
377
    protected void accumulateByArea(List<NamedArea> superAreas, Classification classification,  IProgressMonitor subMonitor, boolean doClearDescriptions) {
413
    protected void accumulateByArea(List<NamedArea> superAreas, ClassificationLookupDTO classificationLookupDao,  IProgressMonitor subMonitor, boolean doClearDescriptions) {
378 414

  
379 415
        int batchSize = 1000;
380 416

  
......
388 424
        List<NamedArea> superAreaList = (List)termService.find(superAreaUuids);
389 425

  
390 426
        // visit all accepted taxa
391
        Pager<Taxon> taxonPager = null;
427
        subMonitor.beginTask("Accumulating by area ",  classificationLookupDao.getTaxonIds().size());
428
        Iterator<Integer> taxonIdIterator = classificationLookupDao.getTaxonIds().iterator();
429

  
392 430
        int pageIndex = 0;
393
        boolean isLastPage = false;
431
        while (taxonIdIterator.hasNext()) {
394 432
        while (!isLastPage) {
395 433

  
396 434
            if(txStatus == null) {
......
398 436
                txStatus = startTransaction(false);
399 437
            }
400 438

  
401
            //TODO limit by classification if not null
402
            taxonPager = taxonService.page(Taxon.class, batchSize, pageIndex++, null, null);
403

  
404
            if(taxonPager.getCurrentIndex() == 0){
405
                subMonitor.beginTask("Accumulating by area ",  taxonPager.getCount().intValue());
439
            // load taxa for this batch
440
            List<TaxonBase> taxa = new ArrayList<TaxonBase>(batchSize);
441
            Set<Integer> taxonIds = new HashSet<Integer>(batchSize);
442
            while(taxonIdIterator.hasNext() && taxonIds.size() < batchSize ) {
443
                taxonIds.add(taxonIdIterator.next());
406 444
            }
407 445

  
408
            logger.debug("accumulateByArea() - taxon " + taxonPager.getFirstRecord() + " to " + taxonPager.getLastRecord() + " of " + taxonPager.getCount() + "]");
446
//            logger.debug("accumulateByArea() - taxon " + taxonPager.getFirstRecord() + " to " + taxonPager.getLastRecord() + " of " + taxonPager.getCount() + "]");
409 447

  
410
            if (taxonPager.getRecords().size() == 0){
411
                break;
412
            }
413
            isLastPage = taxonPager.getRecords().size() < batchSize;
448
            taxa = taxonService.listByIds(taxonIds, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY);
414 449

  
415 450
            // iterate over the taxa and accumulate areas
416
            for(Taxon taxon : taxonPager.getRecords()) {
451
            for(TaxonBase taxon : taxa) {
417 452
                if(logger.isDebugEnabled()){
418
                    logger.debug("accumulateByArea() - taxon :" + taxon.getTitleCache());
453
                    logger.debug("accumulateByArea() - taxon :" + taxonToString(taxon));
419 454
                }
420 455

  
421
                TaxonDescription description = findComputedDescription(taxon, doClearDescriptions);
422
                List<Distribution> distributions = distributionsFor(taxon);
456
                TaxonDescription description = findComputedDescription((Taxon)taxon, doClearDescriptions);
457
                List<Distribution> distributions = distributionsFor((Taxon)taxon);
423 458

  
424 459
                // Step through superAreas for accumulation of subAreas
425 460
                for (NamedArea superArea : superAreaList){
426 461

  
427 462
                    // accumulate all sub area status
428 463
                    PresenceAbsenceTerm accumulatedStatus = null;
464
                    // TODO consider using the TermHierarchyLookup (only in local branch a.kohlbecker)
429 465
                    Set<NamedArea> subAreas = getSubAreasFor(superArea);
430 466
                    for(NamedArea subArea : subAreas){
431 467
                        if(logger.isTraceEnabled()){
432
                            logger.trace("accumulateByArea() - \t\t" + subArea.getLabel());
468
                            logger.trace("accumulateByArea() - \t\t" + termToString(subArea));
433 469
                        }
434 470
                        // step through all distributions for the given subArea
435 471
                        for(Distribution distribution : distributions){
436 472
                            if(distribution.getArea() != null && distribution.getArea().equals(subArea) && distribution.getStatus() != null) {
437 473
                                PresenceAbsenceTerm status = distribution.getStatus();
438 474
                                if(logger.isTraceEnabled()){
439
                                    logger.trace("accumulateByArea() - \t\t" + subArea.getLabel() + ": " + status.getLabel());
475
                                    logger.trace("accumulateByArea() - \t\t" + termToString(subArea) + ": " + termToString(status));
440 476
                                }
441 477
                                // skip all having a status value different of those in byAreaIgnoreStatusList
442 478
                                if (getByAreaIgnoreStatusList().contains(status)){
......
448 484
                    } // next sub area
449 485
                    if (accumulatedStatus != null) {
450 486
                        if(logger.isDebugEnabled()){
451
                            logger.debug("accumulateByArea() - \t >> " + superArea.getLabel() + ": " + accumulatedStatus.getLabel());
487
                            logger.debug("accumulateByArea() - \t >> " + termToString(superArea) + ": " + termToString(accumulatedStatus));
452 488
                        }
453 489
                        // store new distribution element for superArea in taxon description
454 490
                        Distribution newDistribitionElement = Distribution.NewInstance(superArea, accumulatedStatus);
......
464 500

  
465 501
            } // next taxon
466 502

  
467
            taxonPager = null;
468 503
            flushAndClear();
469 504

  
470 505
            // commit for every batch, otherwise the persistent context
......
481 516
        subMonitor.done();
482 517
    }
483 518

  
519
    /**
520
     * @param taxon
521
     * @param logger2
522
     * @return
523
     */
524
    private String taxonToString(TaxonBase taxon) {
525
        if(logger.isTraceEnabled()) {
526
            return taxon.getTitleCache();
527
        } else {
528
            return taxon.toString();
529
        }
530
    }
531

  
532
    /**
533
     * @param taxon
534
     * @param logger2
535
     * @return
536
     */
537
    private String termToString(OrderedTermBase<?> term) {
538
        if(logger.isTraceEnabled()) {
539
            return term.getLabel() + " [" + term.getIdInVocabulary() + "]";
540
        } else {
541
            return term.getIdInVocabulary();
542
        }
543
    }
544

  
484 545
   /**
485 546
    * Step 2: Accumulate by ranks staring from lower rank to upper rank, the status of all children
486 547
    * are accumulated on each rank starting from lower rank to upper rank.
......
520 581
        for (Rank rank : ranks) {
521 582

  
522 583
            if(logger.isDebugEnabled()){
523
                logger.debug("accumulateByRank() - at Rank '" + rank.getLabel() + "'");
584
                logger.debug("accumulateByRank() - at Rank '" + termToString(rank) + "'");
524 585
            }
525 586

  
526 587
            Pager<TaxonNode> taxonPager = null;
......
562 623
                        Taxon taxon = taxonNode.getTaxon();
563 624
                        if (taxaProcessedIds.contains(taxon.getId())) {
564 625
                            if(logger.isDebugEnabled()){
565
                                logger.debug("accumulateByRank() - skipping already processed taxon :" + taxon.getTitleCache());
626
                                logger.debug("accumulateByRank() - skipping already processed taxon :" + taxonToString(taxon));
566 627
                            }
567 628
                            continue;
568 629
                        }
569 630
                        taxaProcessedIds.add(taxon.getId());
570 631
                        if(logger.isDebugEnabled()){
571
                            logger.debug("accumulateByRank() [" + rank.getLabel() + "] - taxon :" + taxon.getTitleCache());
632
                            logger.debug("accumulateByRank() [" + rank.getLabel() + "] - taxon :" + taxonToString(taxon));
572 633
                        }
573 634

  
574 635
                        // Step through direct taxonomic children for accumulation
......
578 639

  
579 640
                            getSession().setReadOnly(taxonNode, true);
580 641
                            if(logger.isTraceEnabled()){
581
                                logger.trace("                   subtaxon :" + subTaxonNode.getTaxon().getTitleCache());
642
                                logger.trace("                   subtaxon :" + taxonToString(subTaxonNode.getTaxon()));
582 643
                            }
583 644

  
584 645
                            for(Distribution distribution : distributionsFor(subTaxonNode.getTaxon()) ) {
......
765 826
     * @return
766 827
     */
767 828
    private List<Distribution> distributionsFor(Taxon taxon) {
768
        return descriptionService
769
                .listDescriptionElementsForTaxon(taxon, null, Distribution.class, null, null, null);
829
        List<Distribution> distributions = new ArrayList<Distribution>();
830
        for(TaxonDescription description: taxon.getDescriptions()) {
831
            for(DescriptionElementBase deb : description.getElements()) {
832
                if(deb instanceof Distribution) {
833
                    distributions.add((Distribution)deb);
834
                }
835
            }
836
        }
837
        return distributions;
770 838
    }
771 839

  
772 840
    /**

Also available in: Unified diff