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.programStrategy.ProgramDao;
27  import net.sumaris.core.dao.administration.user.DepartmentDao;
28  import net.sumaris.core.dao.administration.user.PersonDao;
29  import net.sumaris.core.dao.referential.location.LocationDao;
30  import net.sumaris.core.dao.technical.SortDirection;
31  import net.sumaris.core.dao.technical.model.IEntity;
32  import net.sumaris.core.model.administration.programStrategy.Program;
33  import net.sumaris.core.model.administration.user.Person;
34  import net.sumaris.core.model.data.ObservedLocation;
35  import net.sumaris.core.model.referential.location.Location;
36  import net.sumaris.core.util.Beans;
37  import net.sumaris.core.vo.administration.programStrategy.ProgramFetchOptions;
38  import net.sumaris.core.vo.administration.user.DepartmentVO;
39  import net.sumaris.core.vo.administration.user.PersonVO;
40  import net.sumaris.core.vo.data.DataFetchOptions;
41  import net.sumaris.core.vo.data.ObservedLocationVO;
42  import net.sumaris.core.vo.filter.ObservedLocationFilterVO;
43  import org.apache.commons.collections4.CollectionUtils;
44  import org.apache.commons.lang3.StringUtils;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  import org.springframework.beans.factory.annotation.Autowired;
48  import org.springframework.stereotype.Repository;
49  
50  import javax.persistence.EntityManager;
51  import javax.persistence.TypedQuery;
52  import javax.persistence.criteria.*;
53  import java.sql.Timestamp;
54  import java.util.*;
55  import java.util.stream.Collectors;
56  
57  @Repository("observedLocationDao")
58  public class ObservedLocationDaoImpl extends BaseDataDaoImpl implements ObservedLocationDao {
59  
60      /**
61       * Logger.
62       */
63      private static final Logger log =
64              LoggerFactory.getLogger(ObservedLocationDaoImpl.class);
65  
66      @Autowired
67      private LocationDao locationDao;
68  
69      @Autowired
70      private PersonDao personDao;
71  
72      @Autowired
73      private DepartmentDao departmentDao;
74  
75      @Autowired
76      private ProgramDao programDao;
77  
78      public ObservedLocationDaoImpl() {
79          super();
80      }
81  
82      @Override
83      @SuppressWarnings("unchecked")
84      public List<ObservedLocationVO> getAll(int offset, int size, String sortAttribute, SortDirection sortDirection, DataFetchOptions fetchOptions) {
85  
86          CriteriaBuilder builder = entityManager.getCriteriaBuilder(); //getEntityManager().getCriteriaBuilder();
87          CriteriaQuery<ObservedLocation> query = builder.createQuery(ObservedLocation.class);
88          Root<ObservedLocation> observedLocationRoot = query.from(ObservedLocation.class);
89          query.select(observedLocationRoot)
90                  .distinct(true);
91  
92          // Add sorting
93          if (StringUtils.isNotBlank(sortAttribute)) {
94              Expression<?> sortExpression = observedLocationRoot.get(sortAttribute);
95              query.orderBy(SortDirection.DESC.equals(sortDirection) ?
96                      builder.desc(sortExpression) :
97                      builder.asc(sortExpression)
98              );
99          }
100 
101         return toVOs(entityManager.createQuery(query).
102                 setFirstResult(offset)
103                 .setMaxResults(size)
104                 .getResultList(), fetchOptions);
105     }
106 
107     @Override
108     @SuppressWarnings("unchecked")
109     public List<ObservedLocationVO> findByFilter(ObservedLocationFilterVO filter, int offset, int size, String sortAttribute,
110                                                  SortDirection sortDirection, DataFetchOptions fetchOptions) {
111         Preconditions.checkNotNull(filter);
112         Preconditions.checkArgument(offset >= 0);
113         Preconditions.checkArgument(size > 0);
114 
115         Integer programId = null;
116         if (StringUtils.isNotBlank(filter.getProgramLabel())) {
117             programId = programDao.getByLabel(filter.getProgramLabel()).getId();
118         }
119 
120         // Fetch locations
121         //getEntityManager().enableFetchProfile("with-location");
122 
123         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
124         CriteriaQuery<ObservedLocation> query = builder.createQuery(ObservedLocation.class);
125         Root<ObservedLocation> root = query.from(ObservedLocation.class);
126 
127         ParameterExpression<Date> startDateParam = builder.parameter(Date.class);
128         ParameterExpression<Date> endDateParam = builder.parameter(Date.class);
129         ParameterExpression<Integer> locationIdParam = builder.parameter(Integer.class);
130         ParameterExpression<Integer> programIdParam = builder.parameter(Integer.class);
131 
132         query.select(root)
133                 .where(builder.and(
134                         // Filter: program
135                         builder.or(
136                                 builder.isNull(programIdParam),
137                                 builder.equal(root.get(ObservedLocation.Fields.PROGRAM).get(Program.Fields.ID), programIdParam)
138                         ),
139                         // Filter: startDate
140                         builder.or(
141                                 builder.isNull(startDateParam),
142                                 builder.not(builder.lessThan(root.get(ObservedLocation.Fields.END_DATE_TIME), startDateParam))
143                         ),
144                         // Filter: endDate
145                         builder.or(
146                                 builder.isNull(endDateParam),
147                                 builder.not(builder.greaterThan(root.get(ObservedLocation.Fields.START_DATE_TIME), endDateParam))
148                         ),
149                         // Filter: location
150                         builder.or(
151                                 builder.isNull(locationIdParam),
152                                 builder.equal(root.get(ObservedLocation.Fields.LOCATION).get(Location.Fields.ID), locationIdParam)
153                         )
154                 ));
155 
156         // Add sorting
157         if (StringUtils.isNotBlank(sortAttribute)) {
158             Expression<?> sortExpression = root.get(sortAttribute);
159             query.orderBy(SortDirection.DESC.equals(sortDirection) ?
160                     builder.desc(sortExpression) :
161                     builder.asc(sortExpression)
162             );
163         }
164 
165         TypedQuery<ObservedLocation> q = getEntityManager().createQuery(query)
166                 .setParameter(programIdParam, programId)
167                 .setParameter(startDateParam, filter.getStartDate())
168                 .setParameter(endDateParam, filter.getEndDate())
169                 .setParameter(locationIdParam, filter.getLocationId())
170                 .setFirstResult(offset)
171                 .setMaxResults(size);
172         return toVOs(q.getResultList(), fetchOptions);
173     }
174 
175     @Override
176     public Long countByFilter(ObservedLocationFilterVO filter) {
177 
178         CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
179         CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
180 
181         Integer programId = null;
182         if (filter != null && StringUtils.isNotBlank(filter.getProgramLabel())) {
183             programId = programDao.getByLabel(filter.getProgramLabel()).getId();
184         }
185 
186         ParameterExpression<Date> startDateParam = builder.parameter(Date.class);
187         ParameterExpression<Date> endDateParam = builder.parameter(Date.class);
188         ParameterExpression<Integer> locationIdParam = builder.parameter(Integer.class);
189         ParameterExpression<Integer> programIdParam = builder.parameter(Integer.class);
190 
191         Root<ObservedLocation> root = criteriaQuery.from(ObservedLocation.class);
192         criteriaQuery.select(builder.count(root));
193         if (filter != null) {
194             criteriaQuery.where(builder.and(
195                     // Filter: program
196                     builder.or(
197                             builder.isNull(programIdParam),
198                             builder.equal(root.get(ObservedLocation.Fields.PROGRAM).get(Program.Fields.ID), programIdParam)
199                     ),
200                     // Filter: startDate
201                     builder.or(
202                             builder.isNull(startDateParam),
203                             builder.not(builder.lessThan(root.get(ObservedLocation.Fields.END_DATE_TIME), startDateParam))
204                     ),
205                     // Filter: endDate
206                     builder.or(
207                             builder.isNull(endDateParam),
208                             builder.not(builder.greaterThan(root.get(ObservedLocation.Fields.START_DATE_TIME), endDateParam))
209                     ),
210                     // Filter: location
211                     builder.or(
212                             builder.isNull(locationIdParam),
213                             builder.equal(root.get(ObservedLocation.Fields.LOCATION).get(Location.Fields.ID), locationIdParam)
214                     )
215             ));
216         }
217 
218         if (filter != null) {
219             return getEntityManager()
220                     .createQuery(criteriaQuery)
221                     .setParameter(programIdParam, programId)
222                     .setParameter(startDateParam, filter.getStartDate())
223                     .setParameter(endDateParam, filter.getEndDate())
224                     .setParameter(locationIdParam, filter.getLocationId())
225                     .getSingleResult();
226         } else {
227             return getEntityManager()
228                     .createQuery(criteriaQuery)
229                     .getSingleResult();
230         }
231     }
232 
233     @Override
234     public ObservedLocationVO get(int id) {
235         ObservedLocation entity = get(ObservedLocation.class, id);
236         return toVO(entity, null);
237     }
238 
239     @Override
240     public <T> T get(int id, Class<T> targetClass) {
241         if (targetClass.isAssignableFrom(ObservedLocation.class)) return (T) get(ObservedLocation.class, id);
242         if (targetClass.isAssignableFrom(ObservedLocationVO.class)) return (T) get(id);
243         throw new IllegalArgumentException("Unable to convert into " + targetClass.getName());
244     }
245 
246     @Override
247     public ObservedLocationVO./../../net/sumaris/core/vo/data/ObservedLocationVO.html#ObservedLocationVO">ObservedLocationVO save(ObservedLocationVO source) {
248         Preconditions.checkNotNull(source);
249 
250         EntityManager entityManager = getEntityManager();
251         ObservedLocation entity = null;
252         if (source.getId() != null) {
253             entity = get(ObservedLocation.class, source.getId());
254         }
255         boolean isNew = (entity == null);
256         if (isNew) {
257             entity = new ObservedLocation();
258         } else {
259             // Check update date
260             checkUpdateDateForUpdate(source, entity);
261 
262             // Lock entityName
263             lockForUpdate(entity);
264         }
265 
266         // VO -> Entity
267         observedLocationVOToEntity(source, entity, true);
268 
269         // Update update_dt
270         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
271         entity.setUpdateDate(newUpdateDate);
272 
273         // Save entityName
274         if (isNew) {
275             // Force creation date
276             entity.setCreationDate(newUpdateDate);
277             source.setCreationDate(newUpdateDate);
278 
279             entityManager.persist(entity);
280             source.setId(entity.getId());
281         } else {
282             entityManager.merge(entity);
283         }
284 
285         source.setUpdateDate(newUpdateDate);
286 
287 
288         //session.flush();
289         //session.clear();
290 
291         return source;
292     }
293 
294     @Override
295     public void delete(int id) {
296 
297         log.debug(String.format("Deleting observedLocation {id=%s}...", id));
298         delete(ObservedLocation.class, id);
299     }
300 
301     @Override
302     public ObservedLocationVO toVO(ObservedLocation source) {
303         return toVO(source, null);
304     }
305 
306     @Override
307     public ObservedLocationVO./../net/sumaris/core/vo/data/ObservedLocationVO.html#ObservedLocationVO">ObservedLocationVO control(ObservedLocationVO source) {
308         Preconditions.checkNotNull(source);
309 
310         ObservedLocation entity = get(ObservedLocation.class, source.getId());
311 
312         // Check update date
313         checkUpdateDateForUpdate(source, entity);
314 
315         // Lock entityName
316         lockForUpdate(entity);
317 
318         // TODO CONTROL PROCESS HERE
319         Date controlDate = getDatabaseCurrentTimestamp();
320         entity.setControlDate(controlDate);
321 
322         // Update update_dt
323         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
324         entity.setUpdateDate(newUpdateDate);
325 
326         // Save entityName
327         getEntityManager().merge(entity);
328 
329         // Update source
330         source.setControlDate(controlDate);
331         source.setUpdateDate(newUpdateDate);
332 
333         return source;
334     }
335 
336     @Override
337     public ObservedLocationVO/../net/sumaris/core/vo/data/ObservedLocationVO.html#ObservedLocationVO">ObservedLocationVO validate(ObservedLocationVO source) {
338         Preconditions.checkNotNull(source);
339 
340         ObservedLocation entity = get(ObservedLocation.class, source.getId());
341 
342         // Check update date
343         checkUpdateDateForUpdate(source, entity);
344 
345         // Lock entityName
346 //        lockForUpdate(entity);
347 
348         // TODO VALIDATION PROCESS HERE
349         Date validationDate = getDatabaseCurrentTimestamp();
350         entity.setValidationDate(validationDate);
351 
352         // Update update_dt
353         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
354         entity.setUpdateDate(newUpdateDate);
355 
356         // Save entityName
357 //        getEntityManager().merge(entity);
358 
359         // Update source
360         source.setValidationDate(validationDate);
361         source.setUpdateDate(newUpdateDate);
362 
363         return source;
364     }
365 
366     @Override
367     public ObservedLocationVO./net/sumaris/core/vo/data/ObservedLocationVO.html#ObservedLocationVO">ObservedLocationVO unvalidate(ObservedLocationVO source) {
368         Preconditions.checkNotNull(source);
369 
370         ObservedLocation entity = get(ObservedLocation.class, source.getId());
371 
372         // Check update date
373         checkUpdateDateForUpdate(source, entity);
374 
375         // Lock entityName
376 //        lockForUpdate(entity);
377 
378         // TODO UNVALIDATION PROCESS HERE
379         entity.setValidationDate(null);
380 
381         // Update update_dt
382         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
383         entity.setUpdateDate(newUpdateDate);
384 
385         // Save entityName
386 //        getEntityManager().merge(entity);
387 
388         // Update source
389         source.setValidationDate(null);
390         source.setUpdateDate(newUpdateDate);
391 
392         return source;
393     }
394 
395     /* -- protected methods -- */
396 
397     protected List<ObservedLocationVO> toVOs(List<ObservedLocation> source, DataFetchOptions fetchOptions) {
398         return source.stream()
399                 .map(item -> toVO(item, fetchOptions))
400                 .filter(Objects::nonNull)
401                 .collect(Collectors.toList());
402     }
403 
404 
405     protected ObservedLocationVO toVO(ObservedLocation source, DataFetchOptions fetchOptions) {
406         if (source == null) return null;
407 
408         ObservedLocationVOtionVO.html#ObservedLocationVO">ObservedLocationVO target = new ObservedLocationVO();
409 
410         Beans.copyProperties(source, target);
411 
412         // Remove endDateTime if same as startDateTime
413         if (target.getEndDateTime() != null && target.getEndDateTime().equals(target.getStartDateTime())) {
414             target.setEndDateTime(null);
415         }
416 
417         // Program
418         target.setProgram(programDao.toProgramVO(source.getProgram(),
419                 ProgramFetchOptions.builder().withProperties(false)
420                         .build()));
421 
422 
423         target.setQualityFlagId(source.getQualityFlag().getId());
424 
425         // Location
426         target.setLocation(locationDao.toLocationVO(source.getLocation()));
427 
428         // Recorder department
429         if (fetchOptions == null || fetchOptions.isWithRecorderDepartment()) {
430             DepartmentVO recorderDepartment = departmentDao.toDepartmentVO(source.getRecorderDepartment());
431             target.setRecorderDepartment(recorderDepartment);
432         }
433 
434         // Recorder person
435         if ((fetchOptions == null || fetchOptions.isWithRecorderPerson()) && source.getRecorderPerson() != null) {
436             PersonVO recorderPerson = personDao.toPersonVO(source.getRecorderPerson());
437             target.setRecorderPerson(recorderPerson);
438         }
439 
440         // Observers
441         if ((fetchOptions == null || fetchOptions.isWithObservers()) && CollectionUtils.isNotEmpty(source.getObservers())) {
442             Set<PersonVO> observers = source.getObservers().stream().map(personDao::toPersonVO).collect(Collectors.toSet());
443             target.setObservers(observers);
444         }
445 
446         return target;
447     }
448 
449     protected void observedLocationVOToEntity(ObservedLocationVO source, ObservedLocation target, boolean copyIfNull) {
450 
451         // Copy properties
452         copyRootDataProperties(source, target, copyIfNull);
453 
454         // If endDateTime is empty, fill using startDateTime
455         if (target.getEndDateTime() == null) {
456             target.setEndDateTime(target.getStartDateTime());
457         }
458 
459         // Departure location
460         if (copyIfNull || source.getLocation() != null) {
461             if (source.getLocation() == null || source.getLocation().getId() == null) {
462                 target.setLocation(null);
463             } else {
464                 target.setLocation(load(Location.class, source.getLocation().getId()));
465             }
466         }
467 
468         // Observers
469         if (copyIfNull || source.getObservers() != null) {
470             if (CollectionUtils.isEmpty(source.getObservers())) {
471                 if (CollectionUtils.isNotEmpty(target.getObservers())) {
472                     target.getObservers().clear();
473                 }
474             } else {
475                 Map<Integer, Person> observersToRemove = Beans.splitById(target.getObservers());
476                 source.getObservers().stream()
477                         .map(IEntity::getId)
478                         .forEach(personId -> {
479                             if (observersToRemove.remove(personId) == null) {
480                                 // Add new item
481                                 target.getObservers().add(load(Person.class, personId));
482                             }
483                         });
484 
485                 // Remove deleted tableNames
486                 target.getObservers().removeAll(observersToRemove.values());
487             }
488         }
489     }
490 }