View Javadoc
1   package net.sumaris.core.dao.technical.extraction;
2   
3   /*-
4    * #%L
5    * SUMARiS:: Core
6    * %%
7    * Copyright (C) 2018 - 2019 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 com.google.common.collect.ImmutableList;
27  import com.google.common.collect.Lists;
28  import net.sumaris.core.dao.administration.user.DepartmentDao;
29  import net.sumaris.core.dao.administration.user.PersonDao;
30  import net.sumaris.core.dao.data.DataDaos;
31  import net.sumaris.core.dao.technical.hibernate.HibernateDaoSupport;
32  import net.sumaris.core.model.referential.Status;
33  import net.sumaris.core.model.referential.StatusEnum;
34  import net.sumaris.core.model.technical.extraction.*;
35  import net.sumaris.core.util.Beans;
36  import net.sumaris.core.util.StringUtils;
37  import net.sumaris.core.vo.technical.extraction.*;
38  import org.apache.commons.collections4.CollectionUtils;
39  import org.apache.commons.collections4.MapUtils;
40  import org.apache.commons.lang3.ArrayUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  import org.springframework.beans.factory.annotation.Autowired;
44  import org.springframework.stereotype.Repository;
45  
46  import javax.persistence.EntityManager;
47  import javax.persistence.TypedQuery;
48  import javax.persistence.criteria.CriteriaBuilder;
49  import javax.persistence.criteria.CriteriaQuery;
50  import javax.persistence.criteria.ParameterExpression;
51  import javax.persistence.criteria.Root;
52  import java.sql.Timestamp;
53  import java.util.List;
54  import java.util.Map;
55  import java.util.Objects;
56  import java.util.Optional;
57  import java.util.stream.Collectors;
58  
59  /**
60   * @author Benoit Lavenier <benoit.lavenier@e-is.pro>*
61   */
62  @Repository("extractionProductDao")
63  public class ExtractionProductDaoImpl extends HibernateDaoSupport implements ExtractionProductDao {
64  
65      /**
66       * Logger.
67       */
68      private static final Logger log =
69              LoggerFactory.getLogger(ExtractionProductDaoImpl.class);
70  
71      @Autowired
72      private DepartmentDao departmentDao;
73  
74      @Autowired
75      private PersonDao personDao;
76  
77      @Override
78      public List<ExtractionProductVO> findByFilter(ExtractionProductFilterVO filter, ProductFetchOptions fetchOptions) {
79          Preconditions.checkNotNull(filter);
80          Preconditions.checkArgument(ArrayUtils.isNotEmpty(filter.getStatusIds()));
81  
82          StringBuilder hqlQuery = new StringBuilder();
83          hqlQuery.append("from ExtractionProduct p where p.status.id IN (:statusIds)");
84  
85          if (filter.getDepartmentId() != null) {
86              hqlQuery.append(" and p.recorderDepartment.id = :departmentId");
87          }
88  
89          TypedQuery<ExtractionProduct> query = getEntityManager().createQuery(hqlQuery.toString(), ExtractionProduct.class)
90                  .setParameter("statusIds", ImmutableList.copyOf(filter.getStatusIds()));
91  
92          if (filter.getDepartmentId() != null) {
93              query.setParameter("departmentId", filter.getDepartmentId());
94          }
95  
96          return query
97                  .getResultStream()
98                  .map(p -> toProductVO(p, fetchOptions))
99                  .collect(Collectors.toList());
100     }
101 
102     @Override
103     public ExtractionProductVO getByLabel(String label, ProductFetchOptions fetchOptions) {
104         Preconditions.checkNotNull(label);
105 
106         return toProductVO(getEntityManager().createQuery("from ExtractionProduct p where upper(p.label) =:label", ExtractionProduct.class)
107                         .setParameter("label", label.toUpperCase())
108                         .getSingleResult(),
109                 fetchOptions);
110     }
111 
112     @Override
113     public Optional<ExtractionProductVO> get(int id, ProductFetchOptions fetchOptions) {
114         try {
115             return Optional.ofNullable(toProductVO(get(ExtractionProduct.class, id), fetchOptions));
116         } catch (Exception e) {
117             return Optional.empty();
118         }
119     }
120 
121     @Override
122     public List<ExtractionProductColumnVO> getColumnsByIdAndTableLabel(int id, String tableLabel) {
123 
124         CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
125         CriteriaQuery<ExtractionProductColumn> query = cb.createQuery(ExtractionProductColumn.class);
126         Root<ExtractionProductColumn> root = query.from(ExtractionProductColumn.class);
127 
128         query.select(root);
129 
130         ParameterExpression<Integer> productIdParam = cb.parameter(Integer.class);
131         ParameterExpression<String> tableLabelParam = cb.parameter(String.class);
132 
133         query.where(
134                 cb.and(
135                         cb.equal(root.get(ExtractionProductColumn.Fields.TABLE)
136                                 .get(ExtractionProductTable.Fields.PRODUCT)
137                                 .get(ExtractionProduct.Fields.ID), productIdParam),
138                         cb.equal(root.get(ExtractionProductColumn.Fields.TABLE)
139                                 .get(ExtractionProductTable.Fields.LABEL), tableLabelParam)
140                 )
141         );
142 
143         // Sort by rank order
144         query.orderBy(cb.asc(root.get(ExtractionProductColumn.Fields.RANK_ORDER)));
145 
146         return getEntityManager().createQuery(query)
147                 .setParameter(productIdParam, id)
148                 .setParameter(tableLabelParam, tableLabel)
149                 .getResultStream()
150                 .map(c -> toColumnVO(c, null /*with values*/))
151                 .collect(Collectors.toList());
152     }
153 
154     @Override
155     public ExtractionProductVO/../../../net/sumaris/core/vo/technical/extraction/ExtractionProductVO.html#ExtractionProductVO">ExtractionProductVO save(ExtractionProductVO source) {
156         EntityManager entityManager = getEntityManager();
157         ExtractionProduct entity = null;
158         if (source.getId() != null) {
159             entity = get(ExtractionProduct.class, source.getId());
160         }
161         boolean isNew = (entity == null);
162         if (isNew) {
163             entity = new ExtractionProduct();
164         } else {
165             // Check update date
166             checkUpdateDateForUpdate(source, entity);
167 
168             // Lock entityName
169             lockForUpdate(entity);
170         }
171 
172         // VO -> Entity
173         productVOToEntity(source, entity, true/*IMPORTANT: recorder person/department can be null*/);
174 
175         // Update update_dt
176         Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
177         entity.setUpdateDate(newUpdateDate);
178 
179         // Save entityName
180         if (isNew) {
181             // Force creation date
182             entity.setCreationDate(newUpdateDate);
183             source.setCreationDate(newUpdateDate);
184 
185             entityManager.persist(entity);
186             source.setId(entity.getId());
187         }
188 
189         source.setUpdateDate(newUpdateDate);
190         source.setLabel(entity.getLabel());
191 
192         getSession().flush();
193         getSession().clear();
194 
195         // Save tables
196         saveProductTables(source.getTables(), source.getId(), newUpdateDate);
197 
198         getSession().flush();
199         getSession().clear();
200 
201         // Save stratum
202         saveProductStratum(source.getStratum(), source.getId(), newUpdateDate);
203 
204         // Final merge
205         entityManager.merge(entity);
206 
207         getSession().flush();
208         getSession().clear();
209 
210         return source;
211     }
212 
213     @Override
214     public void delete(int id) {
215         log.debug(String.format("Deleting product {id=%s}...", id));
216         delete(ExtractionProduct.class, id);
217     }
218 
219     /* -- protected method -- */
220 
221     protected void productVOToEntity(ExtractionProductVO source, ExtractionProduct target, boolean copyIfNull) {
222 
223         Beans.copyProperties(source, target);
224 
225         // Make sure label is uppercase
226         if (source.getLabel() != null) {
227             String label = source.getLabel().toUpperCase();
228             target.setLabel(label);
229         }
230 
231         // status
232         if (copyIfNull || source.getStatusId() != null) {
233             if (source.getStatusId() == null) {
234                 target.setStatus(null);
235             } else {
236                 target.setStatus(load(Status.class, source.getStatusId()));
237             }
238         }
239 
240         // Parent
241         if (copyIfNull || source.getParentId() != null) {
242             if (source.getParentId() == null) {
243                 target.setParent(null);
244             } else {
245                 target.setParent(load(ExtractionProduct.class, source.getParentId()));
246             }
247         }
248 
249         // Recorder person
250         DataDaos.copyRecorderPerson(getEntityManager(), source, target, copyIfNull);
251 
252         // Recorder department
253         DataDaos.copyRecorderDepartment(getEntityManager(), source, target, copyIfNull);
254         if (target.getRecorderDepartment() == null && target.getRecorderPerson() != null) {
255             // If nul, use recorder person department
256             target.setRecorderDepartment(target.getRecorderPerson().getDepartment());
257         }
258     }
259 
260     protected void saveProductTables(List<ExtractionProductTableVO> sources, int productId, Timestamp updateDate) {
261         ExtractionProduct parent = get(ExtractionProduct.class, productId);
262 
263         final EntityManager em = getEntityManager();
264         if (CollectionUtils.isEmpty(sources)) {
265             if (parent.getTables() != null) {
266                 List<ExtractionProductTable> toRemove = ImmutableList.copyOf(parent.getTables());
267                 parent.getTables().clear();
268                 toRemove.forEach(em::remove);
269             }
270         } else {
271             Map<String, ExtractionProductTable> existingItems = Beans.splitByProperty(
272                     Beans.getList(parent.getTables()),
273                     ExtractionProductTable.Fields.LABEL);
274             final Status enableStatus = load(Status.class, StatusEnum.ENABLE.getId());
275             if (parent.getTables() == null) {
276                 parent.setTables(Lists.newArrayList());
277             }
278 
279             // Save each table
280             sources.stream()
281                     .filter(Objects::nonNull)
282                     .forEach(source -> {
283                         ExtractionProductTable target = existingItems.remove(source.getLabel());
284                         boolean isNew = (target == null);
285                         if (isNew) {
286                             target = new ExtractionProductTable();
287                         }
288                         Beans.copyProperties(source, target);
289                         target.setProduct(parent);
290                         target.setStatus(enableStatus);
291                         target.setUpdateDate(updateDate);
292                         if (isNew) {
293                             target.setCreationDate(updateDate);
294                             em.persist(target);
295                             source.setId(target.getId());
296                         } else {
297                             em.merge(target);
298                         }
299 
300                         source.setUpdateDate(updateDate);
301 
302                         if (isNew) parent.getTables().add(target);
303                     });
304 
305             getSession().flush();
306 
307             // Save each columns
308             // Important: Skip if not columns, because UI editor not sent columns at all.
309             boolean hasColumns = sources.stream().anyMatch(source -> source != null && source.getColumns() != null);
310             if (hasColumns) {
311                 sources.stream()
312                         .filter(Objects::nonNull)
313                         .forEach(source -> saveProductTableColumns(source.getColumns(), source.getId(), updateDate));
314                 getSession().flush();
315             }
316 
317             // Remove old tables
318             if (MapUtils.isNotEmpty(existingItems)) {
319                 parent.getTables().removeAll(existingItems.values());
320                 existingItems.values().forEach(em::remove);
321             }
322 
323         }
324     }
325 
326     protected void saveProductStratum(List<ExtractionProductStrataVO> sources, int productId, Timestamp updateDate) {
327         ExtractionProduct parent = get(ExtractionProduct.class, productId);
328 
329         final EntityManager em = getEntityManager();
330         if (CollectionUtils.isEmpty(sources)) {
331             if (parent.getStratum() != null) {
332                 List<ExtractionProductStrata> toRemove = ImmutableList.copyOf(parent.getStratum());
333                 parent.getStratum().clear();
334                 toRemove.forEach(em::remove);
335             }
336         } else {
337             Map<String, ExtractionProductStrata> existingItems = Beans.splitByProperty(parent.getStratum(), ExtractionProductStrata.Fields.LABEL);
338             Map<String, ExtractionProductTable> existingTables = Beans.splitByProperty(parent.getTables(), ExtractionProductTable.Fields.LABEL);
339             final Status enableStatus = load(Status.class, StatusEnum.ENABLE.getId());
340             if (parent.getStratum() == null) {
341                 parent.setStratum(Lists.newArrayList());
342             }
343 
344             // Save each table
345             sources.stream()
346                     .filter(Objects::nonNull)
347                     .forEach(source -> {
348                         ExtractionProductStrata target = existingItems.remove(source.getLabel());
349                         boolean isNew = (target == null);
350                         if (isNew) {
351                             target = new ExtractionProductStrata();
352                             Beans.copyProperties(source, target);
353                         }
354                         else {
355                             target.setName(source.getName());
356                             target.setIsDefault(source.getIsDefault());
357                         }
358                         target.setProduct(parent);
359                         target.setStatus(source.getStatusId() != null ? load(Status.class, source.getStatusId()) : enableStatus);
360                         target.setUpdateDate(updateDate);
361 
362                         // Link to table (find by sheet anem, or get as singleton)
363                         ExtractionProductTable table = StringUtils.isNotBlank(source.getSheetName())
364                                 ? existingTables.get(source.getSheetName())
365                                 : (existingTables.size() == 1 ? existingTables.values().iterator().next() : null);
366                         if (table != null) {
367                             target.setTable(table);
368                             target.setTimeColumn(findColumnByName(table, source.getTimeColumnName()));
369                             target.setSpaceColumn(findColumnByName(table, source.getSpaceColumnName()));
370                             target.setAggColumn(findColumnByName(table, source.getAggColumnName()));
371                             target.setTechColumn(findColumnByName(table, source.getTechColumnName()));
372                         }
373                         else {
374                             target.setTable(null);
375                             target.setTimeColumn(null);
376                             target.setSpaceColumn(null);
377                             target.setAggColumn(null);
378                             target.setTechColumn(null);
379                         }
380 
381                         if (isNew) {
382                             target.setCreationDate(updateDate);
383                             em.persist(target);
384                             source.setId(target.getId());
385                         } else {
386                             em.merge(target);
387                         }
388 
389                         source.setUpdateDate(updateDate);
390                     });
391 
392             getSession().flush();
393 
394             // Remove old tables
395             if (MapUtils.isNotEmpty(existingItems)) {
396                 parent.getStratum().removeAll(existingItems.values());
397                 existingItems.values().forEach(em::remove);
398             }
399 
400         }
401     }
402 
403     protected void saveProductTableColumns(List<ExtractionProductColumnVO> sources, int tableId, Timestamp updateDate) {
404         final EntityManager em = getEntityManager();
405 
406         // Load parent
407         ExtractionProductTable parent = get(ExtractionProductTable.class, tableId);
408 
409         if (CollectionUtils.isEmpty(sources)) {
410             if (parent.getColumns() != null) {
411                 List<ExtractionProductColumn> toRemove = ImmutableList.copyOf(parent.getColumns());
412                 parent.getColumns().clear();
413                 toRemove.forEach(em::remove);
414             }
415         } else {
416             Map<String, ExtractionProductColumn> existingItems = Beans.splitByProperty(
417                     Beans.getList(parent.getColumns()),
418                     ExtractionProductColumn.Fields.COLUMN_NAME);
419             if (parent.getColumns() == null) {
420                 parent.setColumns(Lists.newArrayList());
421             }
422 
423             // Save each column
424             sources.stream()
425                     .filter(Objects::nonNull)
426                     .forEach(source -> {
427                         ExtractionProductColumn target = existingItems.remove(source.getColumnName());
428                         boolean isNew = (target == null);
429                         if (isNew) {
430                             target = new ExtractionProductColumn();
431                         }
432                         target.setTable(parent);
433                         Beans.copyProperties(source, target);
434                         target.setLabel(StringUtils.underscoreToChangeCase(source.getColumnName()));
435 
436                         if (isNew) {
437                             em.persist(target);
438                             source.setId(target.getId());
439                         } else {
440                             em.merge(target);
441                         }
442 
443                         if (isNew) parent.getColumns().add(target);
444                     });
445 
446             getSession().flush();
447             //getSession().clear();
448 
449             // Save column values
450             sources.stream()
451                     .filter(Objects::nonNull)
452                     .forEach(source -> saveProductTableValues(source.getValues(), source.getId()));
453 
454             getSession().flush();
455             //getSession().clear();
456 
457             // Remove old tables
458             if (MapUtils.isNotEmpty(existingItems)) {
459                 parent.getColumns().removeAll(existingItems.values());
460                 existingItems.values().forEach(em::remove);
461             }
462 
463             getSession().flush();
464             //getSession().clear();
465         }
466     }
467 
468     protected void saveProductTableValues(List<String> sources, int columnId) {
469         ExtractionProductColumn parent = get(ExtractionProductColumn.class, columnId);
470 
471         final EntityManager em = getEntityManager();
472         if (CollectionUtils.isEmpty(sources)) {
473             if (parent.getValues() != null) {
474                 List<ExtractionProductValue> toRemove = ImmutableList.copyOf(parent.getValues());
475                 parent.getValues().clear();
476                 toRemove.forEach(em::remove);
477             }
478         } else {
479             Map<String, ExtractionProductValue> existingItems = Beans.splitByProperty(
480                     Beans.getList(parent.getValues()),
481                     ExtractionProductValue.Fields.LABEL);
482             if (parent.getValues() == null) {
483                 parent.setValues(Lists.newArrayList());
484             }
485 
486             // Transform each entry into entity
487             sources.stream()
488                     .filter(StringUtils::isNotBlank)
489                     .forEach(valueLabel -> {
490                         ExtractionProductValue target = existingItems.remove(valueLabel);
491                         boolean isNew = (target == null);
492                         if (isNew) {
493                             target = new ExtractionProductValue();
494                         }
495                         target.setColumn(parent);
496                         target.setLabel(valueLabel);
497                         if (isNew) {
498                             em.persist(target);
499                         } else {
500                             em.merge(target);
501                         }
502 
503                         if (isNew) parent.getValues().add(target);
504                     });
505 
506             getSession().flush();
507             //getSession().clear();
508 
509             // Remove old values
510             if (MapUtils.isNotEmpty(existingItems)) {
511                 parent.getValues().removeAll(existingItems.values());
512                 existingItems.values().forEach(em::remove);
513             }
514 
515         }
516     }
517 
518     protected ExtractionProductVO toProductVO(ExtractionProduct source, ProductFetchOptions fetchOptions) {
519         ExtractionProductVOction/ExtractionProductVO.html#ExtractionProductVO">ExtractionProductVO target = new ExtractionProductVO();
520         Beans.copyProperties(source, target);
521 
522         // Status
523         if (source.getStatus() != null) {
524             target.setStatusId(source.getStatus().getId());
525         }
526 
527         // Tables
528         if (fetchOptions == null || fetchOptions.isWithTables()) {
529             if (CollectionUtils.isNotEmpty(source.getTables())) {
530                 List<ExtractionProductTableVO> tables = source.getTables().stream()
531                         .map(t -> toProductTableVO(t, fetchOptions))
532                         .collect(Collectors.toList());
533                 target.setTables(tables);
534             }
535         }
536 
537         // Stratum
538         if (fetchOptions == null || fetchOptions.isWithStratum()) {
539             if (CollectionUtils.isNotEmpty(source.getStratum())) {
540                 List<ExtractionProductStrataVO> stratum = source.getStratum().stream()
541                         .map(t -> toProductStrataVO(t))
542                         .collect(Collectors.toList());
543                 target.setStratum(stratum);
544             }
545         }
546 
547         // Recorder department and person
548         if (fetchOptions == null || fetchOptions.isWithRecorderDepartment()) {
549             target.setRecorderDepartment(departmentDao.toDepartmentVO(source.getRecorderDepartment()));
550         }
551         if (fetchOptions == null || fetchOptions.isWithRecorderPerson()) {
552             target.setRecorderPerson(personDao.toPersonVO(source.getRecorderPerson()));
553         }
554 
555         return target;
556     }
557 
558     protected ExtractionProductTableVO toProductTableVO(ExtractionProductTable source, ProductFetchOptions fetchOptions) {
559         ExtractionProductTableVO/ExtractionProductTableVO.html#ExtractionProductTableVO">ExtractionProductTableVO target = new ExtractionProductTableVO();
560         Beans.copyProperties(source, target);
561 
562         // parent
563         if (source.getProduct() != null) {
564             target.setProductId(source.getProduct().getId());
565         }
566 
567         // Status
568         if (source.getStatus() != null) {
569             target.setStatusId(source.getStatus().getId());
570         }
571 
572         // Columns
573         if (fetchOptions == null || fetchOptions.isWithColumns()) {
574             if (CollectionUtils.isNotEmpty(source.getColumns())) {
575                 target.setColumns(source.getColumns().stream()
576                         .map(c -> toColumnVO(c, fetchOptions))
577                         .collect(Collectors.toList()));
578             }
579         }
580 
581         return target;
582 
583     }
584 
585     protected ExtractionProductStrataVO toProductStrataVO(ExtractionProductStrata source) {
586         ExtractionProductStrataVOExtractionProductStrataVO.html#ExtractionProductStrataVO">ExtractionProductStrataVO target = new ExtractionProductStrataVO();
587         Beans.copyProperties(source, target);
588 
589         // parent
590         if (source.getProduct() != null) {
591             target.setProductId(source.getProduct().getId());
592         }
593 
594         // Status
595         if (source.getStatus() != null) {
596             target.setStatusId(source.getStatus().getId());
597         }
598 
599         // table name
600         if (source.getTable() != null) {
601             target.setSheetName(source.getTable().getLabel());
602         }
603 
604         // Column names
605         if (source.getTimeColumn() != null) {
606             target.setTimeColumnName(source.getTimeColumn().getColumnName());
607         }
608         if (source.getSpaceColumn() != null) {
609             target.setSpaceColumnName(source.getSpaceColumn().getColumnName());
610         }
611         if (source.getTechColumn() != null) {
612             target.setTechColumnName(source.getTechColumn().getColumnName());
613         }
614         if (source.getAggColumn() != null) {
615             target.setAggColumnName(source.getAggColumn().getColumnName());
616         }
617 
618         return target;
619 
620     }
621 
622     protected List<String> toColumnValues(ExtractionProductColumn source) {
623         if (source == null || CollectionUtils.isEmpty(source.getValues())) return null;
624         return source.getValues().stream()
625                 .map(ExtractionProductValue::getLabel)
626                 .collect(Collectors.toList());
627     }
628 
629     protected ExtractionProductColumnVO toColumnVO(ExtractionProductColumn source, ProductFetchOptions fetchOptions) {
630         ExtractionProductColumnVOExtractionProductColumnVO.html#ExtractionProductColumnVO">ExtractionProductColumnVO target = new ExtractionProductColumnVO();
631         Beans.copyProperties(source, target);
632 
633         if (fetchOptions == null || fetchOptions.isWithColumnValues()) {
634             target.setValues(toColumnValues(source));
635         }
636         return target;
637     }
638 
639     protected ExtractionProductColumn findColumnByName(ExtractionProductTable table, String columnName) {
640         if (StringUtils.isBlank(columnName)) return null;
641         return table.getColumns().stream()
642                 .filter(c -> columnName.equalsIgnoreCase(c.getColumnName()))
643                 .findFirst().orElse(null);
644     }
645 
646 }