1 package fr.ifremer.reefdb.dao.referential.taxon;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import com.google.common.collect.*;
27 import fr.ifremer.quadrige3.core.dao.referential.StatusCode;
28 import fr.ifremer.quadrige3.core.dao.referential.TaxonGroupTypeCode;
29 import fr.ifremer.quadrige3.core.dao.referential.taxon.*;
30 import fr.ifremer.quadrige3.core.dao.technical.Assert;
31 import fr.ifremer.quadrige3.core.dao.technical.Dates;
32 import fr.ifremer.quadrige3.core.dao.technical.hibernate.TemporaryDataHelper;
33 import fr.ifremer.quadrige3.core.service.technical.CacheService;
34 import fr.ifremer.quadrige3.synchro.meta.DatabaseColumns;
35 import fr.ifremer.quadrige3.synchro.meta.data.DataSynchroTables;
36 import fr.ifremer.quadrige3.synchro.meta.referential.ReferentialSynchroTables;
37 import fr.ifremer.reefdb.config.ReefDbConfiguration;
38 import fr.ifremer.reefdb.dao.technical.Daos;
39 import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
40 import fr.ifremer.reefdb.dto.ReefDbBeans;
41 import fr.ifremer.reefdb.dto.referential.CitationDTO;
42 import fr.ifremer.reefdb.dto.referential.TaxonDTO;
43 import fr.ifremer.reefdb.dto.referential.TaxonomicLevelDTO;
44 import fr.ifremer.reefdb.service.ReefDbTechnicalException;
45 import org.apache.commons.collections4.CollectionUtils;
46 import org.apache.commons.lang3.StringUtils;
47 import org.apache.commons.logging.Log;
48 import org.apache.commons.logging.LogFactory;
49 import org.hibernate.SessionFactory;
50 import org.hibernate.type.DateType;
51 import org.hibernate.type.IntegerType;
52 import org.hibernate.type.StringType;
53 import org.nuiton.i18n.I18n;
54 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.cache.Cache;
56 import org.springframework.dao.DataRetrievalFailureException;
57 import org.springframework.stereotype.Repository;
58
59 import javax.annotation.Nonnull;
60 import javax.annotation.Resource;
61 import java.time.LocalDate;
62 import java.util.*;
63 import java.util.stream.Collectors;
64
65
66
67
68 @Repository("reefDbTaxonNameDao")
69 public class ReefDbTaxonNameDaoImpl extends TaxonNameDaoImpl implements ReefDbTaxonNameDao {
70
71 private static final Log LOG = LogFactory.getLog(ReefDbTaxonNameDaoImpl.class);
72
73 private static final Multimap<String, String> taxonNameIdColumnsByReferentialTables = ImmutableListMultimap.<String, String>builder()
74 .put(ReferentialSynchroTables.TAXON_GROUP_HISTORICAL_RECORD.name(), DatabaseColumns.TAXON_NAME_ID.name())
75 .put(ReferentialSynchroTables.TAXON_NAME.name(), DatabaseColumns.PARENT_TAXON_NAME_ID.name()).build();
76
77 private static final Multimap<String, String> refTaxonIdColumnsByDataTables = ImmutableListMultimap.<String, String>builder()
78 .put(DataSynchroTables.TAXON_MEASUREMENT.name(), DatabaseColumns.REF_TAXON_ID.name()).build();
79
80 private static final Multimap<String, String> taxonNameIdColumnsByDataTables = ImmutableListMultimap.<String, String>builder()
81 .put(DataSynchroTables.TAXON_MEASUREMENT.name(), DatabaseColumns.TAXON_NAME_ID.name()).build();
82
83 private static final Map<String, String> validDtColumnsByDataTables = ImmutableMap.<String, String>builder()
84 .put(DataSynchroTables.TAXON_MEASUREMENT.name(), DatabaseColumns.TAXON_MEAS_VALID_DT.name()).build();
85
86 @Resource
87 protected CacheService cacheService;
88 @Resource
89 protected ReefDbConfiguration config;
90 @Resource(name = "reefDbTaxonNameDao")
91 protected ReefDbTaxonNameDao loopbackTaxonNameDao;
92 @Resource(name = "referenceTaxonDao")
93 protected ReferenceTaxonDao referenceTaxonDao;
94
95
96
97
98
99
100 @Autowired
101 public ReefDbTaxonNameDaoImpl(SessionFactory sessionFactory) {
102 super(sessionFactory);
103 }
104
105
106
107
108 @Override
109 public List<TaxonDTO> getAllTaxonNames() {
110
111 Cache cacheByReferenceId = cacheService.getCache(TAXON_NAME_BY_REFERENCE_ID_CACHE);
112 Cache cacheById = cacheService.getCache(TAXON_NAME_BY_ID_CACHE);
113
114 Map<Integer, String> taxRefMap = loopbackTaxonNameDao.getTaxRefByTaxonNameId();
115 Map<Integer, String> wormsMap = loopbackTaxonNameDao.getWormsByTaxonNameId();
116
117 Iterator<Object[]> it = queryIterator("allTaxonName");
118
119 long nbTaxonName = 0;
120 long nbRefTaxon = 0;
121 List<TaxonDTO> result = Lists.newArrayList();
122 while (it.hasNext()) {
123 Object[] source = it.next();
124 TaxonDTO taxon = toTaxonDTO(Arrays.asList(source).iterator());
125
126
127 taxon.setTaxRef(taxRefMap.get(taxon.getId()));
128 taxon.setWormsRef(wormsMap.get(taxon.getId()));
129
130 result.add(taxon);
131
132
133 cacheById.put(taxon.getId(), taxon);
134 nbTaxonName++;
135
136 if (taxon.isReferent()) {
137 cacheByReferenceId.put(taxon.getReferenceTaxonId(), taxon);
138 nbRefTaxon++;
139 }
140 }
141
142 if (LOG.isDebugEnabled()) {
143 LOG.debug(String.format("%s Taxon name loaded, %s reference taxon loaded", nbTaxonName, nbRefTaxon));
144 }
145 return ImmutableList.copyOf(result);
146 }
147
148
149
150
151 @Override
152 public TaxonDTO getTaxonNameByReferenceId(int referenceTaxonId) {
153
154 Object[] source = queryUnique("taxonNameByReferenceId", "taxonReferenceId", IntegerType.INSTANCE, referenceTaxonId);
155
156 if (source == null) {
157 throw new DataRetrievalFailureException("Can't load taxon name with reference taxon id = " + referenceTaxonId);
158 }
159
160 return toTaxonDTO(Arrays.asList(source).iterator());
161 }
162
163
164
165
166 @Override
167 public TaxonDTO getTaxonNameById(int taxonId) {
168
169 Object[] source = queryUnique("taxonNameById", "taxonNameId", IntegerType.INSTANCE, taxonId);
170
171 if (source == null) {
172 return null;
173 }
174
175 TaxonDTO taxon = toTaxonDTO(Arrays.asList(source).iterator());
176 fillReferent(taxon);
177 return taxon;
178 }
179
180
181
182
183
184
185 @Override
186 public Multimap<Integer, TaxonDTO> getAllTaxonNamesMapByTaxonGroupId(@Nonnull LocalDate date) {
187
188 Iterator<Object[]> it = queryIterator("taxonNameIdsWithTaxonGroupId",
189 "referenceDate", DateType.INSTANCE, Dates.convertToDate(date, config.getDbTimezone()),
190 "taxonGroupTypeCode", StringType.INSTANCE, TaxonGroupTypeCode.IDENTIFICATION.getValue());
191
192 Multimap<Integer, TaxonDTO> result = ArrayListMultimap.create();
193 while (it.hasNext()) {
194 Object[] source = it.next();
195 Iterator<Object> row = Arrays.asList(source).iterator();
196 Integer taxonGroupId = (Integer) row.next();
197 result.put(taxonGroupId, loopbackTaxonNameDao.getTaxonNameById((Integer) row.next()));
198 }
199 return result;
200 }
201
202
203
204
205 @Override
206 public List<TaxonDTO> getTaxonNamesByIds(List<Integer> taxonIds) {
207
208 if (CollectionUtils.isEmpty(taxonIds)) return new ArrayList<>();
209
210 return loopbackTaxonNameDao.getAllTaxonNames().stream().filter(taxon -> taxonIds.contains(taxon.getId())).collect(Collectors.toList());
211 }
212
213
214
215
216 @Override
217 public void fillTaxonsProperties(List<TaxonDTO> taxons) {
218
219 if (CollectionUtils.isEmpty(taxons)) {
220 return;
221 }
222
223 Map<Integer, String> taxRefMap = loopbackTaxonNameDao.getTaxRefByTaxonNameId();
224 Map<Integer, String> wormsMap = loopbackTaxonNameDao.getWormsByTaxonNameId();
225
226 for (TaxonDTO taxon : taxons) {
227
228
229 fillParentAndReferent(taxon);
230
231
232 if (taxon.isVirtual()) {
233 taxon.setCompositeTaxons(loopbackTaxonNameDao.getCompositeTaxonNames(taxon.getId()));
234 }
235
236
237 taxon.setTaxRef(taxRefMap.get(taxon.getId()));
238 taxon.setWormsRef(wormsMap.get(taxon.getId()));
239
240 }
241 }
242
243 private void fillTaxonProperties(TaxonDTO taxon) {
244
245
246 fillParentAndReferent(taxon);
247
248
249 taxon.setTaxRef(loopbackTaxonNameDao.getTaxRefByTaxonNameId().get(taxon.getId()));
250 taxon.setWormsRef(loopbackTaxonNameDao.getWormsByTaxonNameId().get(taxon.getId()));
251
252 }
253
254
255
256
257 @Override
258 public List<TaxonDTO> findFullTaxonNamesByCriteria(String levelCode, String name, boolean isStrictName, Boolean isLocal) {
259 List<TaxonDTO> result = findTaxonNamesByCriteria(levelCode, name, isStrictName, isLocal);
260
261 fillTaxonsProperties(result);
262
263 return result;
264 }
265
266
267
268
269 @Override
270 public List<TaxonDTO> findTaxonNamesByCriteria(String levelCode, String name, boolean isStrictName, final Boolean isLocal) {
271
272 List<TaxonDTO> result = Lists.newArrayList();
273
274
275 if (StringUtils.isBlank(levelCode) && StringUtils.isBlank(name)) {
276
277 result.addAll(loopbackTaxonNameDao.getAllTaxonNames());
278
279 } else {
280
281 Iterator<Object[]> it = queryIterator("taxonNamesByCriteria",
282 "levelCd", StringType.INSTANCE, levelCode,
283 "name", StringType.INSTANCE, isStrictName ? null : name,
284 "strictName", StringType.INSTANCE, isStrictName ? name : null);
285
286 while (it.hasNext()) {
287 Object[] source = it.next();
288 result.add(toTaxonDTO(Arrays.asList(source).iterator()));
289 }
290 }
291
292 fillReferents(result);
293
294 return ImmutableList.copyOf(isLocal == null
295 ? result
296 : ReefDbBeans.filterReferential(result, isLocal)
297 );
298 }
299
300
301
302
303 @Override
304 public List<TaxonomicLevelDTO> getAllTaxonomicLevels() {
305 Iterator<Object[]> it = queryIterator("allTaxonomicLevels");
306
307 List<TaxonomicLevelDTO> result = Lists.newArrayList();
308 while (it.hasNext()) {
309 Object[] source = it.next();
310 result.add(toTaxonomicLevelDTO(Arrays.asList(source).iterator()));
311 }
312 return result;
313 }
314
315
316
317
318 @Override
319 public List<TaxonDTO> getCompositeTaxonNames(Integer taxonNameId) {
320 Iterator<Object[]> it = queryIterator("compositeTaxonNamesByTaxonNameId",
321 "taxonNameId", IntegerType.INSTANCE, taxonNameId);
322
323 List<TaxonDTO> result = Lists.newArrayList();
324 while (it.hasNext()) {
325 Object[] source = it.next();
326 TaxonDTO taxon = toTaxonDTO(Arrays.asList(source).iterator());
327 fillTaxonProperties(taxon);
328 result.add(taxon);
329 }
330 return result;
331 }
332
333
334
335
336 @Override
337 public Map<Integer, String> getTaxRefByTaxonNameId() {
338
339 return getAlternateReferencesMap(config.getAlternativeTaxonOriginTaxRef());
340 }
341
342
343
344
345 @Override
346 public Map<Integer, String> getWormsByTaxonNameId() {
347
348 return getAlternateReferencesMap(config.getAlternativeTaxonOriginWorms());
349 }
350
351 private Map<Integer, String> getAlternateReferencesMap(String originCode) {
352
353 Map<Integer, String> result = Maps.newHashMap();
354 if (StringUtils.isNotBlank(originCode)) {
355
356 Iterator<Object[]> rows = queryIterator("alternateTaxonCode",
357 "originCode", StringType.INSTANCE, originCode);
358
359 while (rows.hasNext()) {
360 Object[] row = rows.next();
361 result.put((Integer) row[0], (String) row[1]);
362 }
363 }
364
365 return result;
366 }
367
368
369
370
371 @Override
372 public void saveTaxons(List<? extends TaxonDTO> taxons) {
373 if (CollectionUtils.isEmpty(taxons)) {
374 return;
375 }
376
377 for (TaxonDTO taxon : taxons) {
378 if (taxon.isDirty()) {
379 saveTaxon(taxon);
380 taxon.setDirty(false);
381 }
382 }
383 getSession().flush();
384 getSession().clear();
385 }
386
387
388
389
390 @Override
391 public void deleteTaxons(List<Integer> taxonIds) {
392 if (taxonIds == null) return;
393 taxonIds.stream().filter(Objects::nonNull).distinct().forEach(id -> {
394 remove(id);
395
396
397 boolean isLocalTaxon = TemporaryDataHelper.isTemporaryId(id);
398 if (isLocalTaxon) {
399 referenceTaxonDao.remove(id);
400 }
401 });
402 getSession().flush();
403 getSession().clear();
404 }
405
406
407
408
409 @Override
410 public void replaceTemporaryTaxon(Integer sourceId, Integer sourceReferenceId, Integer targetId, Integer targetReferenceId, boolean delete) {
411 Assert.notNull(sourceId);
412 Assert.notNull(targetId);
413 Assert.notNull(sourceReferenceId);
414 Assert.notNull(targetReferenceId);
415
416
417 executeMultipleUpdate(taxonNameIdColumnsByReferentialTables, sourceId, targetId);
418
419
420 executeMultipleUpdateWithNullCondition(taxonNameIdColumnsByDataTables, validDtColumnsByDataTables, sourceId, targetId);
421
422
423 executeMultipleUpdateWithNullCondition(refTaxonIdColumnsByDataTables, validDtColumnsByDataTables, sourceReferenceId, targetReferenceId);
424
425 if (delete) {
426
427 remove(sourceId);
428
429
430 boolean isLocalTaxon = TemporaryDataHelper.isTemporaryId(sourceId);
431 if (isLocalTaxon) {
432 referenceTaxonDao.remove(sourceId);
433 }
434 }
435
436 getSession().flush();
437 getSession().clear();
438
439 }
440
441
442
443
444 @Override
445 public boolean isTaxonNameUsedInReferential(int taxonId) {
446
447 return executeMultipleCount(taxonNameIdColumnsByReferentialTables, taxonId);
448 }
449
450
451
452
453 @Override
454 public boolean isReferenceTaxonUsedInReferential(int refTaxonId, int excludedTaxonNameId) {
455
456 Long countInTaxonName = queryUniqueTyped("countReferenceTaxonInTaxonName",
457 "refTaxonId", IntegerType.INSTANCE, refTaxonId,
458 "excludedTaxonNameId", IntegerType.INSTANCE, excludedTaxonNameId);
459 return countInTaxonName > 0;
460 }
461
462
463
464
465 @Override
466 public boolean isReferenceTaxonUsedInData(int refTaxonId) {
467
468 return executeMultipleCount(refTaxonIdColumnsByDataTables, refTaxonId);
469 }
470
471
472
473
474 @Override
475 public boolean isReferenceTaxonUsedInValidatedData(int refTaxonId) {
476
477 return executeMultipleCountWithNotNullCondition(refTaxonIdColumnsByDataTables, validDtColumnsByDataTables, refTaxonId);
478 }
479
480
481
482 private void saveTaxon(TaxonDTO taxon) {
483 Assert.notNull(taxon);
484 Assert.isTrue(taxon.getId() == null || taxon.getId() < 0);
485 Assert.notBlank(taxon.getName());
486 Assert.notNull(taxon.getParentTaxon());
487 Assert.notNull(taxon.getLevel());
488
489 TaxonName target;
490 if (taxon.getId() == null) {
491 target = TaxonName.Factory.newInstance();
492 target.setTaxonNameId(TemporaryDataHelper.getNewNegativeIdForTemporaryData(getSession(), target.getClass()));
493 } else {
494 target = get(taxon.getId());
495 }
496
497 target.setTaxonNameCompleteNm(taxon.getName());
498 target.setTaxonomicLevel(load(TaxonomicLevelImpl.class, taxon.getLevel().getCode()));
499
500 if (target.getReferenceTaxon() == null) {
501
502 int refTaxonId = target.getTaxonNameId();
503
504
505 if (get(ReferenceTaxonImpl.class, refTaxonId) != null) {
506 throw new ReefDbTechnicalException(I18n.t("reefdb.error.referential.badLocalTaxonNameId", refTaxonId));
507 }
508
509 ReferenceTaxon refTaxon = ReferenceTaxon.Factory.newInstance();
510 refTaxon.setRefTaxonId(refTaxonId);
511 refTaxon.setUpdateDt(newUpdateTimestamp());
512 target.setReferenceTaxon(refTaxon);
513 getSession().save(refTaxon);
514
515
516 taxon.setReferenceTaxonId(refTaxonId);
517 }
518
519 target.setParentTaxonName(load(taxon.getParentTaxon().getId()));
520
521 target.setTaxonNameCm(taxon.getComment());
522 target.setTaxonNameIsRefer(true);
523 target.setTaxonNameTempor(true);
524 target.setTaxonNameIsVirtual(true);
525 target.setTaxonNameObsol(taxon.isObsolete());
526 if (taxon.getCitation() != null) {
527 target.setCitId(load(CitationImpl.class, taxon.getCitation().getId()));
528 } else {
529 target.setCitId(null);
530 }
531
532 getSession().save(target);
533 taxon.setId(target.getTaxonNameId());
534 }
535
536 private TaxonDTO toTaxonDTO(Iterator<Object> source) {
537 TaxonDTO result = ReefDbBeanFactory.newTaxonDTO();
538
539
540 result.setId((Integer) source.next());
541
542
543 result.setName((String) source.next());
544
545 result.setComment((String) source.next());
546
547 result.setReferent((Boolean) source.next());
548 result.setVirtual((Boolean) source.next());
549 result.setObsolete((Boolean) source.next());
550 result.setTemporary((Boolean) source.next());
551
552 result.setReferenceTaxonId((Integer) source.next());
553 result.setParentTaxonId((Integer) source.next());
554
555 String levelCode = (String) source.next();
556 String levelLabel = (String) source.next();
557 String levelName = (String) source.next();
558 Integer levelNb = (Integer) source.next();
559 Integer citationId = (Integer) source.next();
560 String citationName = (String) source.next();
561
562 result.setCreationDate(Daos.convertToDate(source.next()));
563 result.setUpdateDate(Daos.convertToDate(source.next()));
564
565
566 if (levelCode != null) {
567 TaxonomicLevelDTO level = ReefDbBeanFactory.newTaxonomicLevelDTO();
568 level.setCode(levelCode);
569 level.setLabel(levelLabel);
570 level.setName(levelName);
571 level.setNumber(levelNb);
572
573 result.setLevel(level);
574 }
575
576
577 if (citationId != null) {
578 CitationDTO citation = ReefDbBeanFactory.newCitationDTO();
579 citation.setId(citationId);
580 citation.setName(citationName);
581
582 result.setCitation(citation);
583 }
584
585
586 StatusCode statusCode;
587 if (Boolean.TRUE.equals(result.isTemporary())) {
588 statusCode = StatusCode.TEMPORARY;
589 } else {
590 statusCode = StatusCode.ENABLE;
591 }
592 result.setStatus(Daos.getStatus(statusCode));
593
594 return result;
595 }
596
597
598
599
600 @Override
601 public void fillParentAndReferent(TaxonDTO taxon) {
602
603
604 fillReferent(taxon);
605
606
607 if (taxon.getParentTaxonId() != null) {
608 if (taxon.getParentTaxonId().equals(taxon.getId())) {
609 taxon.setParentTaxon(taxon);
610 } else {
611 taxon.setParentTaxon(loopbackTaxonNameDao.getTaxonNameById(taxon.getParentTaxonId()));
612 }
613 }
614 }
615
616 @Override
617 public void fillReferents(List<TaxonDTO> taxons) {
618
619 if (taxons == null) return;
620 taxons.forEach(this::fillReferent);
621 }
622
623 private void fillReferent(TaxonDTO taxon) {
624
625
626 if (taxon.getReferenceTaxon() == null || !Objects.equals(taxon.getReferenceTaxon().getId(), taxon.getReferenceTaxonId())) {
627 taxon.setReferenceTaxon(loopbackTaxonNameDao.getTaxonNameByReferenceId(taxon.getReferenceTaxonId()));
628 }
629 }
630
631 private TaxonomicLevelDTO toTaxonomicLevelDTO(Iterator<Object> source) {
632 TaxonomicLevelDTO result = ReefDbBeanFactory.newTaxonomicLevelDTO();
633
634
635 result.setCode((String) source.next());
636
637
638 result.setLabel((String) source.next());
639
640
641 result.setName((String) source.next());
642
643
644 result.setNumber((Integer) source.next());
645
646 return result;
647
648 }
649 }