View Javadoc
1   package net.sumaris.core.dao.data;
2   
3   /*-
4    * #%L
5    * SUMARiS:: Core
6    * %%
7    * Copyright (C) 2018 SUMARiS Consortium
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Preconditions;
26  import net.sumaris.core.dao.administration.user.PersonDao;
27  import net.sumaris.core.dao.referential.ReferentialDao;
28  import net.sumaris.core.dao.referential.taxon.TaxonNameDao;
29  import net.sumaris.core.model.administration.programStrategy.PmfmStrategy;
30  import net.sumaris.core.model.data.Batch;
31  import net.sumaris.core.model.data.Landing;
32  import net.sumaris.core.model.data.Operation;
33  import net.sumaris.core.model.data.Sample;
34  import net.sumaris.core.model.referential.pmfm.Matrix;
35  import net.sumaris.core.model.referential.pmfm.Unit;
36  import net.sumaris.core.model.referential.taxon.TaxonGroup;
37  import net.sumaris.core.model.referential.taxon.TaxonName;
38  import net.sumaris.core.util.Beans;
39  import net.sumaris.core.vo.administration.programStrategy.ProgramVO;
40  import net.sumaris.core.vo.administration.user.DepartmentVO;
41  import net.sumaris.core.vo.administration.user.PersonVO;
42  import net.sumaris.core.vo.data.LandingVO;
43  import net.sumaris.core.vo.data.OperationVO;
44  import net.sumaris.core.vo.data.SampleVO;
45  import net.sumaris.core.vo.referential.ReferentialVO;
46  import org.apache.commons.collections4.CollectionUtils;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  import org.springframework.beans.factory.annotation.Autowired;
50  import org.springframework.stereotype.Repository;
51  
52  import javax.annotation.PostConstruct;
53  import javax.persistence.EntityManager;
54  import javax.persistence.criteria.CriteriaBuilder;
55  import javax.persistence.criteria.CriteriaQuery;
56  import javax.persistence.criteria.ParameterExpression;
57  import javax.persistence.criteria.Root;
58  import java.sql.Timestamp;
59  import java.util.List;
60  import java.util.Objects;
61  import java.util.stream.Collectors;
62  import java.util.stream.Stream;
63  
64  @Repository("sampleDao")
65  public class SampleDaoImpl extends BaseDataDaoImpl implements SampleDao {
66  
67      /** Logger. */
68      private static final Logger log =
69              LoggerFactory.getLogger(SampleDaoImpl.class);
70  
71      @Autowired
72      private ReferentialDao referentialDao;
73  
74      @Autowired
75      private TaxonNameDao taxonNameDao;
76  
77      @Autowired
78      private PersonDao personDao;
79  
80      private int unitIdNone;
81  
82      @PostConstruct
83      protected void init() {
84          this.unitIdNone = config.getUnitIdNone();
85      }
86  
87      @Override
88      @SuppressWarnings("unchecked")
89      public List<SampleVO> getAllByOperationId(int operationId) {
90  
91          CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
92          CriteriaQuery<Sample> query = cb.createQuery(Sample.class);
93          Root<Sample> root = query.from(Sample.class);
94  
95          query.select(root);
96  
97          ParameterExpression<Integer> tripIdParam = cb.parameter(Integer.class);
98  
99          query.where(cb.equal(root.get(Sample.Fields.OPERATION).get(Operation.Fields.ID), tripIdParam));
100 
101         // Sort by rank order
102         query.orderBy(cb.asc(root.get(PmfmStrategy.Fields.RANK_ORDER)));
103 
104         return toSampleVOs(getEntityManager().createQuery(query)
105                 .setParameter(tripIdParam, operationId).getResultList(), false);
106     }
107 
108     @Override
109     @SuppressWarnings("unchecked")
110     public List<SampleVO> getAllByLandingId(int landingId) {
111 
112         CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
113         CriteriaQuery<Sample> query = cb.createQuery(Sample.class);
114         Root<Sample> root = query.from(Sample.class);
115 
116         query.select(root);
117 
118         ParameterExpression<Integer> idParam = cb.parameter(Integer.class);
119 
120         query.where(cb.equal(root.get(Sample.Fields.LANDING).get(Landing.Fields.ID), idParam));
121 
122         // Sort by rank order
123         query.orderBy(cb.asc(root.get(PmfmStrategy.Fields.RANK_ORDER)));
124 
125         return toSampleVOs(getEntityManager().createQuery(query)
126                 .setParameter(idParam, landingId).getResultList(), false);
127     }
128 
129 
130     @Override
131     public SampleVO get(int id) {
132         Sample entity = get(Sample.class, id);
133         return toSampleVO(entity, false);
134     }
135 
136     @Override
137     public List<SampleVO> saveByOperationId(int operationId, List<SampleVO> sources) {
138 
139         // Load parent entity
140         Operation parent = get(Operation.class, operationId);
141         ProgramVOprogramStrategy/ProgramVO.html#ProgramVO">ProgramVO parentProgram = new ProgramVO();
142         parentProgram.setId(parent.getTrip().getProgram().getId());
143 
144         // Remember existing entities
145         final List<Integer> sourcesIdsToRemove = Beans.collectIds(Beans.getList(parent.getSamples()));
146 
147         // Save each entities
148         List<SampleVO> result = sources.stream().map(source -> {
149             source.setOperationId(operationId);
150             source.setProgram(parentProgram);
151             if (source.getId() != null) {
152                 sourcesIdsToRemove.remove(source.getId());
153             }
154             return save(source);
155         }).collect(Collectors.toList());
156 
157         // Remove unused entities
158         if (CollectionUtils.isNotEmpty(sourcesIdsToRemove)) {
159             sourcesIdsToRemove.forEach(this::delete);
160         }
161 
162         // Remove parent (use only parentId)
163         result.stream().forEach(sample -> {
164             if (sample.getParent() != null) {
165                 sample.setParentId(sample.getParent().getId());
166                 sample.setParent(null);
167             }
168         });
169 
170         return result;
171     }
172 
173     @Override
174     public List<SampleVO> saveByLandingId(int landingId, List<SampleVO> sources) {
175         // Load parent entity
176         Landing parent = get(Landing.class, landingId);
177         ProgramVOprogramStrategy/ProgramVO.html#ProgramVO">ProgramVO parentProgram = new ProgramVO();
178         parentProgram.setId(parent.getProgram().getId());
179 
180         // Remember existing entities
181         final List<Integer> sourcesIdsToRemove = Beans.collectIds(Beans.getList(parent.getSamples()));
182 
183         // Save each entities
184         List<SampleVO> result = sources.stream().map(source -> {
185             source.setLandingId(landingId);
186             source.setProgram(parentProgram);
187             if (source.getId() != null) {
188                 sourcesIdsToRemove.remove(source.getId());
189             }
190             return save(source);
191         }).collect(Collectors.toList());
192 
193         // Remove unused entities
194         if (CollectionUtils.isNotEmpty(sourcesIdsToRemove)) {
195             sourcesIdsToRemove.forEach(this::delete);
196         }
197 
198         // Remove parent (use only parentId)
199         result.stream().forEach(sample -> {
200             if (sample.getParent() != null) {
201                 sample.setParentId(sample.getParent().getId());
202                 sample.setParent(null);
203             }
204         });
205 
206         return result;
207     }
208 
209     @Override
210     public SampleVOf="../../../../../net/sumaris/core/vo/data/SampleVO.html#SampleVO">SampleVO save(SampleVO source) {
211         Preconditions.checkNotNull(source);
212 
213         EntityManager entityManager = getEntityManager();
214         Sample entity = null;
215         if (source.getId() != null) {
216             entity = get(Sample.class, source.getId());
217         }
218         boolean isNew = (entity == null);
219         if (isNew) {
220             entity = new Sample();
221         }
222 
223         if (!isNew) {
224             // Check update date
225             // TODO: check why SUMARiS app did not refresh sample's updateDate after a first save
226             //checkUpdateDateForUpdate(source, entity);
227 
228             // Lock entityName
229             //lockForUpdate(entity);
230         }
231 
232         // Copy some fields from the trip
233         copySomeFieldsFromParent(source);
234 
235         // VO -> Entity
236         sampleVOToEntity(source, entity, true);
237 
238         // Update update_dt
239         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
240         entity.setUpdateDate(newUpdateDate);
241 
242         // Save entity
243         if (isNew) {
244             // Force creation date
245             entity.setCreationDate(newUpdateDate);
246             source.setCreationDate(newUpdateDate);
247 
248             entityManager.persist(entity);
249             source.setId(entity.getId());
250         } else {
251             if (entity.getCreationDate() == null) {
252                 log.warn("Recording a sample without creation date. Should never occur! Sample ID=" + entity.getId());
253                 entity.setCreationDate(newUpdateDate);
254                 source.setCreationDate(newUpdateDate);
255             }
256             entityManager.merge(entity);
257         }
258 
259         source.setUpdateDate(newUpdateDate);
260 
261         // Update link to parent
262         if (source.getParentId() == null && entity.getParent() != null) {
263             source.setParentId(entity.getParent().getId());
264         }
265 
266         entityManager.flush();
267         entityManager.clear();
268 
269         return source;
270     }
271 
272     @Override
273     public void delete(int id) {
274 
275         log.debug(String.format("Deleting sample {id=%s}...", id));
276         delete(Sample.class, id);
277     }
278 
279     @Override
280     public SampleVO toSampleVO(Sample source) {
281         return toSampleVO(source, true);
282     }
283 
284 
285     /* -- protected methods -- */
286 
287     protected SampleVO toSampleVO(Sample source, boolean allFields) {
288 
289         if (source == null) return null;
290 
291         SampleVOmpleVO.html#SampleVO">SampleVO target = new SampleVO();
292 
293         Beans.copyProperties(source, target);
294 
295         // Matrix
296         ReferentialVO matrix = referentialDao.toReferentialVO(source.getMatrix());
297         target.setMatrix(matrix);
298 
299         // Size Unit
300         if (source.getSizeUnit() != null && source.getSizeUnit().getId().intValue() != unitIdNone) {
301             target.setSizeUnit(source.getSizeUnit().getLabel());
302         }
303 
304         // Taxon group
305         if (source.getTaxonGroup() != null) {
306             ReferentialVO taxonGroup = referentialDao.toReferentialVO(source.getTaxonGroup());
307             target.setTaxonGroup(taxonGroup);
308         }
309 
310         // Taxon name (from reference)
311         if (source.getReferenceTaxon() != null) {
312             ReferentialVO taxonName = taxonNameDao.getTaxonNameReferent(source.getReferenceTaxon().getId());
313             target.setTaxonName(taxonName);
314         }
315 
316         // Parent sample
317         if (source.getParent() != null) {
318             target.setParentId(source.getParent().getId());
319         }
320 
321         // Operation
322         if (source.getOperation() != null) {
323             target.setOperationId(source.getOperation().getId());
324         }
325         // Batch
326         if (source.getBatch() != null) {
327             target.setBatchId(source.getBatch().getId());
328         }
329 
330         // If full export
331         if (allFields) {
332             // Recorder department
333             DepartmentVO recorderDepartment = referentialDao.toTypedVO(source.getRecorderDepartment(), DepartmentVO.class).orElse(null);
334             target.setRecorderDepartment(recorderDepartment);
335 
336             // Recorder person
337             if (source.getRecorderPerson() != null) {
338                 PersonVO recorderPerson = personDao.toPersonVO(source.getRecorderPerson());
339                 target.setRecorderPerson(recorderPerson);
340             }
341         }
342 
343         return target;
344     }
345 
346     protected void copySomeFieldsFromParent(SampleVO target) {
347         OperationVO operation = target.getOperation();
348         if (operation != null) {
349             target.setRecorderDepartment(operation.getRecorderDepartment());
350             return;
351         }
352         LandingVO landing = target.getLanding();
353         if (landing != null) {
354             target.setRecorderDepartment(landing.getRecorderDepartment());
355             return;
356         }
357     }
358 
359     protected List<SampleVO> toSampleVOs(List<Sample> source, boolean allFields) {
360         return this.toSampleVOs(source.stream(), allFields);
361     }
362 
363     protected List<SampleVO> toSampleVOs(Stream<Sample> source, boolean allFields) {
364         return source.map(s -> this.toSampleVO(s, allFields))
365                 .filter(Objects::nonNull)
366                 .collect(Collectors.toList());
367     }
368 
369     protected void sampleVOToEntity(SampleVO source, Sample target, boolean copyIfNull) {
370 
371         copyRootDataProperties(source, target, copyIfNull);
372 
373         // Matrix
374         if (copyIfNull || source.getMatrix() != null) {
375             if (source.getMatrix() == null || source.getMatrix().getId() == null) {
376                 target.setMatrix(null);
377             }
378             else {
379                 target.setMatrix(load(Matrix.class, source.getMatrix().getId()));
380             }
381         }
382 
383         // Size Unit
384         if (copyIfNull || source.getSizeUnit() != null) {
385             if (source.getSizeUnit() == null) {
386                 target.setSizeUnit(null);
387             }
388             else {
389                 ReferentialVO unit = referentialDao.findByUniqueLabel(Unit.class.getSimpleName(), source.getSizeUnit());
390                 Preconditions.checkNotNull(unit, String.format("Invalid 'sample.sizeUnit': unit symbol '%s' not exists", source.getSizeUnit()));
391                 target.setSizeUnit(load(Unit.class, unit.getId()));
392             }
393         }
394 
395         // Taxon group
396         if (copyIfNull || source.getTaxonGroup() != null) {
397             if (source.getTaxonGroup() == null || source.getTaxonGroup().getId() == null) {
398                 target.setTaxonGroup(null);
399             }
400             else {
401                 target.setTaxonGroup(load(TaxonGroup.class, source.getTaxonGroup().getId()));
402             }
403         }
404 
405         // Reference taxon (from taxon name)
406         if (copyIfNull || source.getTaxonName() != null) {
407             if (source.getTaxonName() == null || source.getTaxonName().getId() == null) {
408                 target.setReferenceTaxon(null);
409             }
410             else {
411                 // Get the taxon name, then set reference taxon
412                 TaxonName taxonname = get(TaxonName.class, source.getTaxonName().getId());
413                 target.setReferenceTaxon(taxonname.getReferenceTaxon());
414             }
415         }
416 
417         Integer parentId = source.getParent() != null ? source.getParent().getId() : source.getParentId();
418         Integer opeId = source.getOperationId() != null ? source.getOperationId() : (source.getOperation() != null ? source.getOperation().getId() : null);
419         Integer landingId = source.getLandingId() != null ? source.getLandingId() : (source.getLanding() != null ? source.getLanding().getId() : null);
420 
421         // Parent sample
422         if (copyIfNull || (parentId != null)) {
423             if (parentId == null) {
424                 target.setParent(null);
425             }
426             else {
427                 Sample parent = load(Sample.class, parentId);
428                 target.setParent(parent);
429 
430                 // Force operation from parent's operation
431                 opeId = parent.getOperation().getId();
432             }
433         }
434 
435         // Operation
436         if (copyIfNull || (opeId != null)) {
437             if (opeId == null) {
438                 target.setOperation(null);
439             } else {
440                 target.setOperation(load(Operation.class, opeId));
441             }
442         }
443 
444         // Landing
445         if (copyIfNull || (landingId != null)) {
446             if (landingId == null) {
447                 target.setLanding(null);
448             } else {
449                 target.setLanding(load(Landing.class, landingId));
450             }
451         }
452 
453         // Batch
454         Integer batchId = source.getBatchId() != null ? source.getBatchId() : (source.getBatch() != null ? source.getBatch().getId() : null);
455         if (copyIfNull || (batchId != null)) {
456             if (batchId == null) {
457                 target.setBatch(null);
458             }
459             else {
460                 target.setBatch(load(Batch.class, batchId));
461             }
462         }
463     }
464 }