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.config.SumarisConfiguration;
27  import net.sumaris.core.dao.administration.programStrategy.ProgramDao;
28  import net.sumaris.core.dao.administration.user.DepartmentDao;
29  import net.sumaris.core.dao.administration.user.PersonDao;
30  import net.sumaris.core.dao.referential.location.LocationDao;
31  import net.sumaris.core.dao.technical.SortDirection;
32  import net.sumaris.core.model.QualityFlagEnum;
33  import net.sumaris.core.model.administration.programStrategy.Program;
34  import net.sumaris.core.model.data.Trip;
35  import net.sumaris.core.model.referential.QualityFlag;
36  import net.sumaris.core.model.referential.location.Location;
37  import net.sumaris.core.util.Beans;
38  import net.sumaris.core.vo.administration.programStrategy.ProgramFetchOptions;
39  import net.sumaris.core.vo.administration.user.DepartmentVO;
40  import net.sumaris.core.vo.administration.user.PersonVO;
41  import net.sumaris.core.vo.data.DataFetchOptions;
42  import net.sumaris.core.vo.data.TripVO;
43  import net.sumaris.core.vo.data.VesselSnapshotVO;
44  import net.sumaris.core.vo.filter.TripFilterVO;
45  import org.apache.commons.collections4.CollectionUtils;
46  import org.apache.commons.lang3.StringUtils;
47  import org.hibernate.Session;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  import org.springframework.beans.factory.annotation.Autowired;
51  import org.springframework.dao.DataRetrievalFailureException;
52  import org.springframework.stereotype.Repository;
53  
54  import javax.persistence.EntityManager;
55  import javax.persistence.TypedQuery;
56  import javax.persistence.criteria.*;
57  import java.sql.Timestamp;
58  import java.util.Date;
59  import java.util.List;
60  import java.util.Objects;
61  import java.util.Set;
62  import java.util.stream.Collectors;
63  
64  @Repository("tripDao")
65  public class TripDaoImpl extends BaseDataDaoImpl implements TripDao {
66  
67      /**
68       * Logger.
69       */
70      private static final Logger log =
71              LoggerFactory.getLogger(TripDaoImpl.class);
72  
73      @Autowired
74      private SumarisConfiguration config;
75  
76      @Autowired
77      private LocationDao locationDao;
78  
79      @Autowired
80      private PersonDao personDao;
81  
82      @Autowired
83      private DepartmentDao departmentDao;
84  
85      @Autowired
86      private ProgramDao programDao;
87  
88      public TripDaoImpl() {
89          super();
90      }
91  
92      @Override
93      @SuppressWarnings("unchecked")
94      public List<TripVO> findAll(int offset, int size, String sortAttribute,
95                                  SortDirection sortDirection,
96                                  DataFetchOptions fieldOptions) {
97  
98          CriteriaBuilder builder = entityManager.getCriteriaBuilder(); //getEntityManager().getCriteriaBuilder();
99          CriteriaQuery<Trip> query = builder.createQuery(Trip.class);
100         Root<Trip> tripRoot = query.from(Trip.class);
101         query.select(tripRoot)
102                 .distinct(true);
103 
104         // Add sorting
105         if (StringUtils.isNotBlank(sortAttribute)) {
106             Expression<?> sortExpression = tripRoot.get(sortAttribute);
107             query.orderBy(SortDirection.DESC.equals(sortDirection) ?
108                     builder.desc(sortExpression) :
109                     builder.asc(sortExpression)
110             );
111         }
112 
113         // Enable fetch profiles
114         Session session = getSession();
115         if (fieldOptions.isWithRecorderDepartment() || fieldOptions.isWithRecorderPerson())
116             session.enableFetchProfile(Trip.FETCH_PROFILE_RECORDER);
117         if (fieldOptions.isWithObservers())
118             session.enableFetchProfile(Trip.FETCH_PROFILE_OBSERVERS);
119         session.enableFetchProfile(Trip.FETCH_PROFILE_LOCATION);
120 
121         return toTripVOs(entityManager.createQuery(query)
122                 .setFirstResult(offset)
123                 .setMaxResults(size)
124                 .getResultList(), fieldOptions);
125     }
126 
127     @Override
128     @SuppressWarnings("unchecked")
129     public List<TripVO> findAll(TripFilterVO filter, int offset, int size, String sortAttribute,
130                                 SortDirection sortDirection,
131                                 DataFetchOptions fieldOptions) {
132         Preconditions.checkNotNull(filter);
133         Preconditions.checkArgument(offset >= 0);
134         Preconditions.checkArgument(size > 0);
135 
136         Integer programId = null;
137         if (StringUtils.isNotBlank(filter.getProgramLabel())) {
138             programId = programDao.getByLabel(filter.getProgramLabel()).getId();
139         }
140 
141         // Fetch locations
142         //getEntityManager().enableFetchProfile("with-location");
143 
144         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
145         CriteriaQuery<Trip> query = builder.createQuery(Trip.class);
146         Root<Trip> root = query.from(Trip.class);
147 
148         ParameterExpression<Date> startDateParam = builder.parameter(Date.class);
149         ParameterExpression<Date> endDateParam = builder.parameter(Date.class);
150         ParameterExpression<Integer> locationIdParam = builder.parameter(Integer.class);
151         ParameterExpression<Integer> programIdParam = builder.parameter(Integer.class);
152         ParameterExpression<Integer> vesselIdParam = builder.parameter(Integer.class);
153 
154         query.select(root)
155                 .where(builder.and(
156                         // Filter: program
157                         builder.or(
158                                 builder.isNull(programIdParam),
159                                 builder.equal(root.get(Trip.Fields.PROGRAM).get(Program.Fields.ID), programIdParam)
160                         ),
161                         // Filter: startDate
162                         builder.or(
163                                 builder.isNull(startDateParam),
164                                 builder.not(builder.lessThan(root.get(Trip.Fields.RETURN_DATE_TIME), startDateParam))
165                         ),
166                         // Filter: endDate
167                         builder.or(
168                                 builder.isNull(endDateParam),
169                                 builder.not(builder.greaterThan(root.get(Trip.Fields.DEPARTURE_DATE_TIME), endDateParam))
170                         ),
171                         // Filter: location
172                         builder.or(
173                                 builder.isNull(locationIdParam),
174                                 builder.equal(root.get(Trip.Fields.DEPARTURE_LOCATION).get(Location.Fields.ID), locationIdParam),
175                                 builder.equal(root.get(Trip.Fields.RETURN_LOCATION).get(Location.Fields.ID), locationIdParam)
176                         ),
177                         // Filter: vessel
178                         builder.or(
179                                 builder.isNull(vesselIdParam),
180                                 builder.equal(root.get(Trip.Fields.VESSEL).get(Location.Fields.ID), vesselIdParam)
181                         )
182                 ));
183 
184         // Add sorting
185         if (StringUtils.isNotBlank(sortAttribute)) {
186             Expression<?> sortExpression = root.get(sortAttribute);
187             query.orderBy(SortDirection.DESC.equals(sortDirection) ?
188                     builder.desc(sortExpression) :
189                     builder.asc(sortExpression)
190             );
191         }
192 
193         TypedQuery<Trip> q = getEntityManager().createQuery(query)
194                 .setParameter(programIdParam, programId)
195                 .setParameter(startDateParam, filter.getStartDate())
196                 .setParameter(endDateParam, filter.getEndDate())
197                 .setParameter(locationIdParam, filter.getLocationId())
198                 .setParameter(vesselIdParam, filter.getVesselId())
199                 .setFirstResult(offset)
200                 .setMaxResults(size);
201         return toTripVOs(q.getResultList(), fieldOptions);
202     }
203 
204     @Override
205     public Long countByFilter(TripFilterVO filter) {
206 
207         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
208         CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
209 
210         Integer programId = null;
211         if (filter != null && StringUtils.isNotBlank(filter.getProgramLabel())) {
212             programId = programDao.getByLabel(filter.getProgramLabel()).getId();
213         }
214 
215         ParameterExpression<Date> startDateParam = builder.parameter(Date.class);
216         ParameterExpression<Date> endDateParam = builder.parameter(Date.class);
217         ParameterExpression<Integer> locationIdParam = builder.parameter(Integer.class);
218         ParameterExpression<Integer> programIdParam = builder.parameter(Integer.class);
219 
220         Root<Trip> root = criteriaQuery.from(Trip.class);
221         criteriaQuery.select(builder.count(root));
222         if (filter != null) {
223             criteriaQuery.where(builder.and(
224                     // Filter: program
225                     builder.or(
226                             builder.isNull(programIdParam),
227                             builder.equal(root.get(Trip.Fields.PROGRAM).get(Program.Fields.ID), programIdParam)
228                     ),
229                     // Filter: startDate
230                     builder.or(
231                             builder.isNull(startDateParam),
232                             builder.not(builder.lessThan(root.get(Trip.Fields.RETURN_DATE_TIME), startDateParam))
233                     ),
234                     // Filter: endDate
235                     builder.or(
236                             builder.isNull(endDateParam),
237                             builder.not(builder.greaterThan(root.get(Trip.Fields.DEPARTURE_DATE_TIME), endDateParam))
238                     ),
239                     // Filter: location
240                     builder.or(
241                             builder.isNull(locationIdParam),
242                             builder.equal(root.get(Trip.Fields.DEPARTURE_LOCATION).get(Location.Fields.ID), locationIdParam),
243                             builder.equal(root.get(Trip.Fields.RETURN_LOCATION).get(Location.Fields.ID), locationIdParam)
244                     )
245             ));
246         }
247 
248         if (filter != null) {
249             return getEntityManager()
250                     .createQuery(criteriaQuery)
251                     .setParameter(programIdParam, programId)
252                     .setParameter(startDateParam, filter.getStartDate())
253                     .setParameter(endDateParam, filter.getEndDate())
254                     .setParameter(locationIdParam, filter.getLocationId())
255                     .getSingleResult();
256         } else {
257             return getEntityManager()
258                     .createQuery(criteriaQuery)
259                     .getSingleResult();
260         }
261     }
262 
263     @Override
264     public TripVO get(int id) {
265         Trip entity = get(Trip.class, id);
266         return toVO(entity);
267     }
268 
269     @Override
270     public TripVOref="../../../../../net/sumaris/core/vo/data/TripVO.html#TripVO">TripVO save(TripVO source) {
271         Preconditions.checkNotNull(source);
272 
273         EntityManager entityManager = getEntityManager();
274         Trip entity = null;
275         if (source.getId() != null && source.getId().intValue() >= 0) {
276             entity = get(Trip.class, source.getId());
277         }
278         boolean isNew = (entity == null);
279         if (isNew) {
280             entity = new Trip();
281         } else {
282             // Check update date
283             checkUpdateDateForUpdate(source, entity);
284 
285             // Lock entityName
286             lockForUpdate(entity);
287         }
288 
289         // VO -> Entity
290         tripVOToEntity(source, entity, true);
291 
292         // Update update_dt
293         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
294         entity.setUpdateDate(newUpdateDate);
295 
296         // Save entityName
297         if (isNew) {
298             // Force id to null, to use the generator
299             entity.setId(null);
300 
301             // Force creation date
302             entity.setCreationDate(newUpdateDate);
303             source.setCreationDate(newUpdateDate);
304 
305             entityManager.persist(entity);
306             source.setId(entity.getId());
307         } else {
308             entityManager.merge(entity);
309         }
310 
311         source.setUpdateDate(newUpdateDate);
312 
313 
314         //session.flush();
315         //session.clear();
316 
317         return source;
318     }
319 
320     @Override
321     public void delete(int id) {
322 
323         log.debug(String.format("Deleting trip {id=%s}...", id));
324         delete(Trip.class, id);
325     }
326 
327     @Override
328     public TripVO toVO(Trip source) {
329         return toTripVO(source, null);
330     }
331 
332     @Override
333     public TripVO="../../../../../net/sumaris/core/vo/data/TripVO.html#TripVO">TripVO control(TripVO source) {
334         Preconditions.checkNotNull(source);
335 
336         Trip entity = get(Trip.class, source.getId());
337         if (entity == null) {
338             throw new DataRetrievalFailureException(String.format("Trip {%s} not found", source.getId()));
339         }
340 
341         // Check update date
342         checkUpdateDateForUpdate(source, entity);
343 
344         // Lock entityName
345         lockForUpdate(entity);
346 
347         // TODO CONTROL PROCESS HERE
348         Date controlDate = getDatabaseCurrentTimestamp();
349         entity.setControlDate(controlDate);
350 
351         // Update update_dt
352         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
353         entity.setUpdateDate(newUpdateDate);
354 
355         // Save entityName
356         getEntityManager().merge(entity);
357 
358         // Update source
359         source.setControlDate(controlDate);
360         source.setUpdateDate(newUpdateDate);
361 
362         return source;
363     }
364 
365     @Override
366     public TripVO"../../../../../net/sumaris/core/vo/data/TripVO.html#TripVO">TripVO validate(TripVO source) {
367         Preconditions.checkNotNull(source);
368 
369         Trip entity = get(Trip.class, source.getId());
370         if (entity == null) {
371             throw new DataRetrievalFailureException(String.format("Trip {%s} not found", source.getId()));
372         }
373 
374         // Check update date
375         checkUpdateDateForUpdate(source, entity);
376 
377         // Lock entityName
378         // lockForUpdate(entity);
379 
380         // Update update_dt
381         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
382         entity.setUpdateDate(newUpdateDate);
383 
384         // TODO VALIDATION PROCESS HERE
385         entity.setValidationDate(newUpdateDate);
386 
387         // Save entityName
388         getEntityManager().merge(entity);
389 
390         // Update source
391         source.setValidationDate(newUpdateDate);
392         source.setUpdateDate(newUpdateDate);
393 
394         return source;
395     }
396 
397     @Override
398     public TripVO./../../../../net/sumaris/core/vo/data/TripVO.html#TripVO">TripVO unvalidate(TripVO source) {
399         Preconditions.checkNotNull(source);
400 
401         Trip entity = get(Trip.class, source.getId());
402         if (entity == null) {
403             throw new DataRetrievalFailureException(String.format("Trip {%s} not found", source.getId()));
404         }
405 
406         // Check update date
407         checkUpdateDateForUpdate(source, entity);
408 
409         // Lock entityName
410 //        lockForUpdate(entity);
411 
412         // TODO UNVALIDATION PROCESS HERE
413         entity.setValidationDate(null);
414         entity.setQualificationDate(null);
415         entity.setQualityFlag(load(QualityFlag.class, QualityFlagEnum.NOT_QUALIFED.getId()));
416 
417         // Update update_dt
418         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
419         entity.setUpdateDate(newUpdateDate);
420 
421         // Save entityName
422         getEntityManager().merge(entity);
423 
424         // Update source
425         source.setValidationDate(null);
426         source.setQualificationDate(null);
427         source.setQualityFlagId(QualityFlagEnum.NOT_QUALIFED.getId());
428         source.setUpdateDate(newUpdateDate);
429 
430         return source;
431     }
432 
433     @Override
434     public TripVO="../../../../../net/sumaris/core/vo/data/TripVO.html#TripVO">TripVO qualify(TripVO source) {
435         Preconditions.checkNotNull(source);
436 
437         Trip entity = get(Trip.class, source.getId());
438         if (entity == null) {
439             throw new DataRetrievalFailureException(String.format("Trip {%s} not found", source.getId()));
440         }
441 
442         // Check update date
443         checkUpdateDateForUpdate(source, entity);
444 
445         // Lock entityName
446 //        lockForUpdate(entity);
447 
448 
449         // Update update_dt
450         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
451         entity.setUpdateDate(newUpdateDate);
452 
453         int qualityFlagId = source.getQualityFlagId() != null ? source.getQualityFlagId().intValue() : 0;
454 
455         // If not qualify, then remove the qualification date
456         if (qualityFlagId == QualityFlagEnum.NOT_QUALIFED.getId()) {
457             entity.setQualificationDate(null);
458         }
459         else {
460             entity.setQualificationDate(newUpdateDate);
461         }
462         // Apply a get, because can return a null value (e.g. if id is not in the DB instance)
463         entity.setQualityFlag(get(QualityFlag.class, Integer.valueOf(qualityFlagId)));
464 
465         // TODO UNVALIDATION PROCESS HERE
466         // - insert into qualification history
467 
468         // Save entityName
469         getEntityManager().merge(entity);
470 
471         // Update source
472         source.setQualificationDate(entity.getQualificationDate());
473         source.setQualityFlagId(entity.getQualityFlag() != null ? entity.getQualityFlag().getId() : 0);
474         source.setUpdateDate(newUpdateDate);
475 
476         return source;
477     }
478 
479     /* -- protected methods -- */
480 
481     protected List<TripVO> toTripVOs(List<Trip> source, DataFetchOptions fieldOptions) {
482         return source.stream()
483                 .map(item -> this.toTripVO(item, fieldOptions))
484                 .filter(Objects::nonNull)
485                 .collect(Collectors.toList());
486     }
487 
488     protected TripVO toTripVO(Trip source, DataFetchOptions fieldOptions) {
489         if (source == null) return null;
490 
491         TripVOTripVO.html#TripVO">TripVO target = new TripVO();
492 
493         Beans.copyProperties(source, target);
494 
495         // Program
496         target.setProgram(programDao.toProgramVO(source.getProgram(),
497                 ProgramFetchOptions.builder().withProperties(false).build()));
498 
499         // Vessel
500         VesselSnapshotVOtml#VesselSnapshotVO">VesselSnapshotVO vesselSnapshot = new VesselSnapshotVO();
501         vesselSnapshot.setId(source.getVessel().getId());
502         target.setVesselSnapshot(vesselSnapshot);
503         target.setQualityFlagId(source.getQualityFlag().getId());
504 
505         // Departure & return locations
506         target.setDepartureLocation(locationDao.toLocationVO(source.getDepartureLocation()));
507         target.setReturnLocation(locationDao.toLocationVO(source.getReturnLocation()));
508 
509         // Recorder department
510         if ((fieldOptions == null || fieldOptions.isWithRecorderDepartment()) && source.getRecorderDepartment() != null) {
511             DepartmentVO recorderDepartment = departmentDao.toDepartmentVO(source.getRecorderDepartment());
512             target.setRecorderDepartment(recorderDepartment);
513         }
514 
515         // Recorder person
516         if ((fieldOptions == null || fieldOptions.isWithRecorderPerson()) && source.getRecorderPerson() != null) {
517             PersonVO recorderPerson = personDao.toPersonVO(source.getRecorderPerson());
518             target.setRecorderPerson(recorderPerson);
519         }
520 
521         // Observers
522         if ((fieldOptions == null || fieldOptions.isWithObservers()) && CollectionUtils.isNotEmpty(source.getObservers())) {
523             Set<PersonVO> observers = source.getObservers().stream().map(personDao::toPersonVO).collect(Collectors.toSet());
524             target.setObservers(observers);
525         }
526 
527         return target;
528     }
529 
530     protected void tripVOToEntity(TripVO source, Trip target, boolean copyIfNull) {
531         // Copy properties
532         copyRootDataProperties(source, target, copyIfNull);
533 
534         // Observers
535         copyObservers(source, target, copyIfNull);
536 
537         // Vessel
538         copyVessel(source, target, copyIfNull);
539 
540         // Departure location
541         if (copyIfNull || source.getDepartureLocation() != null) {
542             if (source.getDepartureLocation() == null || source.getDepartureLocation().getId() == null) {
543                 target.setDepartureLocation(null);
544             } else {
545                 target.setDepartureLocation(load(Location.class, source.getDepartureLocation().getId()));
546             }
547         }
548 
549         // Return location
550         if (copyIfNull || source.getReturnLocation() != null) {
551             if (source.getReturnLocation() == null || source.getReturnLocation().getId() == null) {
552                 target.setReturnLocation(null);
553             } else {
554                 target.setReturnLocation(load(Location.class, source.getReturnLocation().getId()));
555             }
556         }
557 
558     }
559 }