View Javadoc
1   package fr.ifremer.reefdb.dao.referential.taxon;
2   
3   /*
4    * #%L
5    * Reef DB :: Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
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.StatusImpl;
29  import fr.ifremer.quadrige3.core.dao.referential.TaxonGroupTypeCode;
30  import fr.ifremer.quadrige3.core.dao.referential.TaxonGroupTypeImpl;
31  import fr.ifremer.quadrige3.core.dao.referential.taxon.TaxonGroup;
32  import fr.ifremer.quadrige3.core.dao.referential.taxon.TaxonGroupDaoImpl;
33  import fr.ifremer.quadrige3.core.dao.referential.taxon.TaxonGroupHistoricalRecord;
34  import fr.ifremer.quadrige3.core.dao.referential.taxon.TaxonNameImpl;
35  import fr.ifremer.quadrige3.core.dao.technical.Assert;
36  import fr.ifremer.quadrige3.core.dao.technical.hibernate.TemporaryDataHelper;
37  import fr.ifremer.quadrige3.core.service.technical.CacheService;
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.TaxonDTO;
42  import fr.ifremer.reefdb.dto.referential.TaxonGroupDTO;
43  import org.apache.commons.collections4.CollectionUtils;
44  import org.hibernate.Query;
45  import org.hibernate.SessionFactory;
46  import org.hibernate.type.IntegerType;
47  import org.hibernate.type.StringType;
48  import org.springframework.beans.factory.annotation.Autowired;
49  import org.springframework.cache.Cache;
50  import org.springframework.cache.interceptor.SimpleKey;
51  import org.springframework.dao.DataRetrievalFailureException;
52  import org.springframework.stereotype.Repository;
53  
54  import javax.annotation.Resource;
55  import java.time.LocalDate;
56  import java.util.*;
57  import java.util.stream.Collectors;
58  
59  /**
60   * <p>ReefDbTaxonGroupDaoImpl class.</p>
61   *
62   */
63  @Repository("reefDbTaxonGroupDao")
64  public class ReefDbTaxonGroupDaoImpl extends TaxonGroupDaoImpl implements ReefDbTaxonGroupDao {
65  
66  //    private static final Log log = LogFactory.getLog(ReefDbTaxonGroupDaoImpl.class);
67  
68      private static final Multimap<String, String> columnNamesByReferentialTableNames = ImmutableListMultimap.<String, String>builder()
69              .put("TAXON_GROUP", "PARENT_TAXON_GROUP_ID")
70              .put("TAXON_GROUP_HISTORICAL_RECORD", "TAXON_GROUP_ID").build();
71  
72      private static final Multimap<String, String> columnNamesByDataTableNames = ImmutableListMultimap.<String, String>builder()
73              .put("TAXON_MEASUREMENT", "TAXON_GROUP_ID").build();
74  
75      private static final Map<String, String> validationDateColumnNameByDataTableNames = ImmutableMap.<String, String>builder()
76              .put("TAXON_MEASUREMENT", "TAXON_MEAS_VALID_DT").build();
77  
78      @Resource
79      protected CacheService cacheService;
80  
81      @Resource(name = "reefDbTaxonGroupDao")
82      protected ReefDbTaxonGroupDao loopbackTaxonGroupDao;
83      @Resource(name = "reefDbTaxonNameDao")
84      protected ReefDbTaxonNameDao taxonNameDao;
85  
86      /**
87       * <p>Constructor for ReefDbTaxonGroupDaoImpl.</p>
88       *
89       * @param sessionFactory a {@link org.hibernate.SessionFactory} object.
90       */
91      @Autowired
92      public ReefDbTaxonGroupDaoImpl(SessionFactory sessionFactory) {
93          super(sessionFactory);
94      }
95  
96      /** {@inheritDoc} */
97      @Override
98      public List<TaxonGroupDTO> getAllTaxonGroups() {
99  
100         // get all needed cache to update
101         Cache cacheById = cacheService.getCache(TAXON_GROUP_BY_ID_CACHE);
102         Cache allTaxonNamesCache = cacheService.getCache(ReefDbTaxonNameDao.ALL_TAXON_NAMES_CACHE);
103         Cache taxonCache = cacheService.getCache(ReefDbTaxonNameDao.TAXON_NAME_BY_ID_CACHE);
104         Cache referenceTaxonCache = cacheService.getCache(ReefDbTaxonNameDao.TAXON_NAME_BY_REFERENCE_ID_CACHE);
105         Cache taxonNameByTaxonGroupIdCache = cacheService.getCache(ReefDbTaxonNameDao.TAXON_NAME_BY_TAXON_GROUP_ID_CACHE);
106 
107         // get all taxon names
108         List<TaxonDTO> allTaxonNames = taxonNameDao.getAllTaxonNames();
109         // get all taxon names map
110         LocalDate taxonRefDate = LocalDate.now();
111         Multimap<Integer, TaxonDTO> taxonMap = taxonNameDao.getAllTaxonNamesMapByTaxonGroupId(taxonRefDate);
112 
113         Query query = createQuery("allTaxonGroup",
114                 "taxonGroupTypeCode", StringType.INSTANCE, TaxonGroupTypeCode.IDENTIFICATION.getValue());
115         Iterator<Object[]> it = Daos.queryIteratorWithStatus(query,
116                 ImmutableList.of(StatusCode.ENABLE.getValue(), StatusCode.TEMPORARY.getValue(), StatusCode.LOCAL_ENABLE.getValue()));
117 
118         // TODO : control TAXON_GROUP_EXCLUS, TAXON_GROUP_UPDATE ?
119         List<TaxonGroupDTO> result = Lists.newArrayList();
120         while (it.hasNext()) {
121             Object[] source = it.next();
122             TaxonGroupDTO taxonGroup = toTaxonGroupDTO(Arrays.asList(source).iterator());
123 
124             // Clone this taxon group and remove parent & children
125             TaxonGroupDTO lightTaxonGroup = getLightTaxonGroup(taxonGroup);
126 
127             // add this taxon group to all taxon
128             Collection<TaxonDTO> taxons = taxonMap.get(taxonGroup.getId());
129             for (TaxonDTO taxon : taxons) {
130 
131                 // add this taxon group
132                 if (!taxon.containsTaxonGroups(lightTaxonGroup)) {
133                     taxon.addTaxonGroups(lightTaxonGroup);
134                 }
135 
136                 // need to update taxonNameById cache
137                 {
138                     TaxonDTO cachedTaxon = taxonCache.get(taxon.getId(), TaxonDTO.class);
139                     if (cachedTaxon != null && !cachedTaxon.containsTaxonGroups(lightTaxonGroup)) {
140                         cachedTaxon.addTaxonGroups(lightTaxonGroup);
141                         taxonCache.evict(taxon.getId());
142                         taxonCache.put(taxon.getId(), cachedTaxon);
143                     }
144                 }
145 
146                 // need to update reference taxon cache
147                 {
148                     TaxonDTO cachedReferenceTaxon = referenceTaxonCache.get(taxon.getReferenceTaxonId(), TaxonDTO.class);
149                     if (cachedReferenceTaxon != null && !cachedReferenceTaxon.containsTaxonGroups(lightTaxonGroup)) {
150                         cachedReferenceTaxon.addTaxonGroups(lightTaxonGroup);
151                         referenceTaxonCache.evict(taxon.getReferenceTaxonId());
152                         referenceTaxonCache.put(taxon.getReferenceTaxonId(), cachedReferenceTaxon);
153                     }
154                 }
155 
156                 // need to update allTaxonNames cache also
157                 {
158                     TaxonDTO cachedTaxon = ReefDbBeans.findById(allTaxonNames, taxon.getId());
159                     if (!cachedTaxon.containsTaxonGroups(lightTaxonGroup)) {
160                         cachedTaxon.addTaxonGroups(lightTaxonGroup);
161                     }
162                 }
163 
164                 // add taxon parent and referent
165                 taxonNameDao.fillParentAndReferent(taxon);
166             }
167 
168             // add taxon names
169             taxonGroup.addAllTaxons(taxons);
170 
171             // add to cache by id
172             cacheById.put(taxonGroup.getId(), taxonGroup);
173 
174             result.add(taxonGroup);
175         }
176 
177         // update taxonNameByTaxonGroupId cache
178         taxonNameByTaxonGroupIdCache.clear();
179         taxonNameByTaxonGroupIdCache.put(taxonRefDate, taxonMap);
180 
181         // update allTaxonNames cache
182         allTaxonNamesCache.put(SimpleKey.EMPTY, allTaxonNames);
183 
184         return ImmutableList.copyOf(result);
185     }
186 
187     /** {@inheritDoc} */
188     @Override
189     public TaxonGroupDTO getTaxonGroupById(int taxonGroupId) {
190 
191         // TODO : control TAXON_GROUP_EXCLUS, TAXON_GROUP_UPDATE ?
192         Object[] source = queryUnique("taxonGroupById", "taxonGroupId", IntegerType.INSTANCE, taxonGroupId);
193 
194         if (source == null) {
195             throw new DataRetrievalFailureException("can't load taxon group with id = " + taxonGroupId);
196         }
197 
198         TaxonGroupDTO taxonGroup = toTaxonGroupDTO(Arrays.asList(source).iterator());
199 
200         // Clone this taxon group and remove parent & children
201         TaxonGroupDTO lightTaxonGroup = getLightTaxonGroup(taxonGroup);
202 
203         Collection<TaxonDTO> taxons = taxonNameDao.getAllTaxonNamesMapByTaxonGroupId(LocalDate.now()).get(taxonGroupId);
204         for (TaxonDTO taxon : taxons) {
205             if (!taxon.containsTaxonGroups(lightTaxonGroup)) {
206                 taxon.addTaxonGroups(lightTaxonGroup);
207             }
208         }
209 
210 //        taxonGroup.setTaxons(ReefDbBeans.getList(taxons));
211         taxonGroup.addAllTaxons(taxons);
212         return taxonGroup;
213     }
214 
215     /** {@inheritDoc}
216      * @param taxonGroupIds*/
217     @Override
218     public List<TaxonGroupDTO> getTaxonGroupsByIds(Collection<Integer> taxonGroupIds) {
219 
220         if (CollectionUtils.isEmpty(taxonGroupIds)) return new ArrayList<>();
221 
222         return loopbackTaxonGroupDao.getAllTaxonGroups().stream().filter(taxonGroup -> taxonGroupIds.contains(taxonGroup.getId())).collect(Collectors.toList());
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public List<TaxonGroupDTO> findTaxonGroups(Integer parentTaxonGroupId, String label, String name, boolean isStrictName, List<String> statusCodes) {
228         // get all taxon names map
229         Multimap<Integer, TaxonDTO> taxonMap = taxonNameDao.getAllTaxonNamesMapByTaxonGroupId(LocalDate.now());
230 
231         Query query = createQuery("taxonGroupsByCriteria",
232                 "parentTaxonGroupId", IntegerType.INSTANCE, parentTaxonGroupId,
233                 "label", StringType.INSTANCE, label,
234                 "name", StringType.INSTANCE, isStrictName ? null : name,
235                 "strictName", StringType.INSTANCE, isStrictName ? name : null,
236                 "taxonGroupTypeCode", StringType.INSTANCE, TaxonGroupTypeCode.IDENTIFICATION.getValue());
237 
238         Iterator<Object[]> it = Daos.queryIteratorWithStatus(query, statusCodes);
239 
240         List<TaxonGroupDTO> result = Lists.newArrayList();
241         while (it.hasNext()) {
242             Object[] source = it.next();
243             TaxonGroupDTO taxonGroup = toTaxonGroupDTO(Arrays.asList(source).iterator());
244             TaxonGroupDTO lightTaxonGroup = getLightTaxonGroup(taxonGroup);
245 
246             // add this taxon group to all taxon
247             Collection<TaxonDTO> taxons = taxonMap.get(taxonGroup.getId());
248             for (TaxonDTO taxon : taxons) {
249                 if (!taxon.containsTaxonGroups(lightTaxonGroup)) {
250                     taxon.addTaxonGroups(lightTaxonGroup);
251                 }
252             }
253 
254             taxonGroup.addAllTaxons(taxons);
255             result.add(taxonGroup);
256         }
257 
258         // Mantis #0027042: when parentTaxonGroupId is set, do a recursive search to populate sub groups
259         if (CollectionUtils.isNotEmpty(result) && parentTaxonGroupId != null) {
260             Set<TaxonGroupDTO> subTaxonGroups = Sets.newHashSet();
261             for (TaxonGroupDTO taxonGroup : result) {
262                 // populate sub groups
263                 subTaxonGroups.addAll(findTaxonGroups(taxonGroup.getId(), label, name, isStrictName, statusCodes));
264             }
265             result.addAll(subTaxonGroups);
266         }
267 
268         return ImmutableList.copyOf(result);
269     }
270 
271     /** {@inheritDoc} */
272     @Override
273     public void saveTaxonGroups(List<? extends TaxonGroupDTO> taxonGroups) {
274         if (CollectionUtils.isEmpty(taxonGroups)) {
275             return;
276         }
277 
278         for (TaxonGroupDTO taxonGroup : taxonGroups) {
279             if (taxonGroup.isDirty()) {
280                 saveTaxonGroup(taxonGroup);
281                 taxonGroup.setDirty(false);
282             }
283         }
284         getSession().flush();
285         getSession().clear();
286     }
287 
288     /** {@inheritDoc} */
289     @Override
290     public void deleteTaxonGroups(List<Integer> taxonGroupIds) {
291         if (taxonGroupIds == null) return;
292         taxonGroupIds.stream().filter(Objects::nonNull).distinct().forEach(this::remove);
293         getSession().flush();
294         getSession().clear();
295     }
296 
297     /** {@inheritDoc} */
298     @Override
299     public void replaceTemporaryTaxonGroup(Integer sourceId, Integer targetId, boolean delete) {
300         Assert.notNull(sourceId);
301         Assert.notNull(targetId);
302 
303         executeMultipleUpdate(columnNamesByReferentialTableNames, sourceId, targetId);
304         executeMultipleUpdateWithNullCondition(columnNamesByDataTableNames, validationDateColumnNameByDataTableNames, sourceId, targetId);
305 
306         if (delete) {
307             // delete temporary
308             remove(sourceId);
309         }
310 
311         getSession().flush();
312         getSession().clear();
313     }
314 
315     /** {@inheritDoc} */
316     @Override
317     public boolean isTaxonGroupUsedInReferential(int taxonGroupId) {
318 
319         return executeMultipleCount(columnNamesByReferentialTableNames, taxonGroupId);
320     }
321 
322     /** {@inheritDoc} */
323     @Override
324     public boolean isTaxonGroupUsedInData(int taxonGroupId) {
325 
326         return executeMultipleCount(columnNamesByDataTableNames, taxonGroupId);
327     }
328 
329     /** {@inheritDoc} */
330     @Override
331     public boolean isTaxonGroupUsedInValidatedData(int taxonGroupId) {
332 
333         return executeMultipleCountWithNotNullCondition(columnNamesByDataTableNames, validationDateColumnNameByDataTableNames, taxonGroupId);
334     }
335 
336     // INTERNAL METHODS
337     private void saveTaxonGroup(TaxonGroupDTO taxonGroup) {
338         Assert.notNull(taxonGroup);
339         Assert.notBlank(taxonGroup.getName());
340 
341         if (taxonGroup.getStatus() == null) {
342             taxonGroup.setStatus(Daos.getStatus(StatusCode.LOCAL_ENABLE));
343         }
344         Assert.isTrue(ReefDbBeans.isLocalStatus(taxonGroup.getStatus()), "source must have local status");
345 
346         TaxonGroup target;
347         if (taxonGroup.getId() == null) {
348             target = TaxonGroup.Factory.newInstance();
349             target.setTaxonGroupId(TemporaryDataHelper.getNewNegativeIdForTemporaryData(getSession(), target.getClass()));
350         } else {
351             target = get(taxonGroup.getId());
352             Assert.isTrue(ReefDbBeans.isLocalStatus(target.getStatus()), "target must have local status");
353         }
354 
355         target.setTaxonGroupNm(taxonGroup.getName());
356         target.setStatus(load(StatusImpl.class, taxonGroup.getStatus().getCode()));
357         target.setTaxonGroupType(load(TaxonGroupTypeImpl.class, "1"));
358         target.setTaxonGroupUpdate(Daos.convertToString(true));
359         target.setTaxonGroupExclus(Daos.convertToString(false));
360 
361         target.setTaxonGroupLb(taxonGroup.getLabel());
362         if (taxonGroup.getParentTaxonGroup() != null) {
363             target.setParentTaxonGroup(load(taxonGroup.getParentTaxonGroup().getId()));
364         } else {
365             target.setParentTaxonGroup(null);
366         }
367         target.setTaxonGroupCm(taxonGroup.getComment());
368         if (target.getTaxonGroupCreationDt() == null) {
369             target.setTaxonGroupCreationDt(newCreateDate());
370         }
371         target.setUpdateDt(newUpdateTimestamp());
372 
373         getSession().save(target);
374         taxonGroup.setId(target.getTaxonGroupId());
375 
376         // taxons
377         saveTaxonsInTaxonGroup(taxonGroup, target);
378 
379 
380     }
381 
382     private void saveTaxonsInTaxonGroup(TaxonGroupDTO source, TaxonGroup target) {
383 
384         Collection<TaxonGroupHistoricalRecord> existingTaxonGroupHistoricalRecords = target.getTaxonGroupHistoricalRecords();
385         Map<Integer, TaxonGroupHistoricalRecord> tghrByTaxonId = ReefDbBeans.mapByProperty(existingTaxonGroupHistoricalRecords, "taxonName.taxonNameId");
386         boolean needUpdate = false;
387 
388         for (TaxonDTO taxon : source.getTaxons()) {
389             TaxonGroupHistoricalRecord tghr = tghrByTaxonId.remove(taxon.getId());
390 
391             if (tghr == null) {
392                 tghr = TaxonGroupHistoricalRecord.Factory.newInstance();
393                 tghr.setTaxonGroupHistRecordId((Integer) TemporaryDataHelper.getNewNegativeIdForTemporaryData(getSession(), TaxonGroupHistoricalRecord.class));
394                 tghr.setTaxonName(load(TaxonNameImpl.class, taxon.getId()));
395                 tghr.setTaxonGroup(target);
396                 getSession().save(tghr);
397                 target.addTaxonGroupHistoricalRecords(tghr);
398                 needUpdate = true;
399             }
400         }
401 
402         if (!tghrByTaxonId.isEmpty()) {
403             // remove remaining
404             for (TaxonGroupHistoricalRecord tghrToRemove : tghrByTaxonId.values()) {
405                 target.removeTaxonGroupHistoricalRecords(tghrToRemove);
406                 getSession().delete(tghrToRemove);
407             }
408             needUpdate = true;
409         }
410 
411         if (needUpdate) {
412 
413             update(target);
414 
415             // get all taxon names map
416             Multimap<Integer, TaxonDTO> taxonMap = taxonNameDao.getAllTaxonNamesMapByTaxonGroupId(LocalDate.now());
417             taxonMap.replaceValues(source.getId(), source.getTaxons());
418 
419             // update taxonNameByTaxonGroupId cache
420             Cache taxonNameByTaxonGroupIdCache = cacheService.getCache(ReefDbTaxonNameDao.TAXON_NAME_BY_TAXON_GROUP_ID_CACHE);
421             taxonNameByTaxonGroupIdCache.evict(null);
422             taxonNameByTaxonGroupIdCache.put(null, taxonMap);
423         }
424     }
425 
426     private TaxonGroupDTO toTaxonGroupDTO(Iterator<Object> source) {
427         TaxonGroupDTO result = ReefDbBeanFactory.newTaxonGroupDTO();
428         result.setId((Integer) source.next());
429         result.setLabel((String) source.next());
430         result.setName((String) source.next());
431         result.setComment((String) source.next());
432         result.setExclusive(Daos.safeConvertToBoolean(source.next()));
433         result.setUpdate(Daos.safeConvertToBoolean(source.next()));
434         result.setType((String) source.next());
435 
436         // parent
437         Integer parentId = (Integer) source.next();
438         String parentLabel = (String) source.next();
439         String parentName = (String) source.next();
440 
441         if (parentId != null) {
442             TaxonGroupDTO parent = ReefDbBeanFactory.newTaxonGroupDTO();
443             parent.setId(parentId);
444             parent.setLabel(parentLabel);
445             parent.setName(parentName);
446 
447             result.setParentTaxonGroup(parent);
448         }
449 
450         result.setStatus(Daos.getStatus((String) source.next()));
451         result.setCreationDate(Daos.convertToDate(source.next()));
452         result.setUpdateDate(Daos.convertToDate(source.next()));
453 
454         return result;
455     }
456 
457     private TaxonGroupDTO getLightTaxonGroup(TaxonGroupDTO taxonGroup) {
458         TaxonGroupDTO result = ReefDbBeans.clone(taxonGroup);
459         result.setTaxons(null);
460         result.setParentTaxonGroup(null);
461         return result;
462     }
463 
464 }