1 package net.sumaris.core.dao.administration.programStrategy;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 com.google.common.collect.Maps;
29 import net.sumaris.core.dao.referential.ReferentialDao;
30 import net.sumaris.core.dao.referential.taxon.TaxonGroupRepository;
31 import net.sumaris.core.dao.technical.SortDirection;
32 import net.sumaris.core.dao.technical.hibernate.HibernateDaoSupport;
33 import net.sumaris.core.model.administration.programStrategy.*;
34 import net.sumaris.core.model.referential.IReferentialEntity;
35 import net.sumaris.core.model.referential.Status;
36 import net.sumaris.core.model.referential.StatusEnum;
37 import net.sumaris.core.model.referential.gear.Gear;
38 import net.sumaris.core.model.referential.taxon.TaxonGroup;
39 import net.sumaris.core.util.Beans;
40 import net.sumaris.core.vo.administration.programStrategy.ProgramFetchOptions;
41 import net.sumaris.core.vo.administration.programStrategy.ProgramVO;
42 import net.sumaris.core.vo.filter.ProgramFilterVO;
43 import net.sumaris.core.vo.referential.ReferentialVO;
44 import net.sumaris.core.vo.referential.TaxonGroupVO;
45 import org.apache.commons.collections4.MapUtils;
46 import org.apache.commons.lang3.StringUtils;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.beans.factory.annotation.Autowired;
50 import org.springframework.dao.EmptyResultDataAccessException;
51 import org.springframework.stereotype.Repository;
52
53 import javax.annotation.PostConstruct;
54 import javax.persistence.EntityManager;
55 import javax.persistence.LockModeType;
56 import javax.persistence.NoResultException;
57 import javax.persistence.TypedQuery;
58 import javax.persistence.criteria.*;
59 import java.sql.Timestamp;
60 import java.util.*;
61 import java.util.stream.Collectors;
62
63 @Repository("programDao")
64 public class ProgramDaoImpl extends HibernateDaoSupport implements ProgramDao {
65
66
67 private static final Logger log =
68 LoggerFactory.getLogger(ProgramDaoImpl.class);
69
70 @Autowired
71 private TaxonGroupRepository taxonGroupRepository;
72
73 @Autowired
74 private ReferentialDao referentialDao;
75
76 @PostConstruct
77 protected void init() {
78 Arrays.stream(ProgramEnum.values()).forEach(programEnum -> {
79 try {
80 ProgramVO program = getByLabel(programEnum.name());
81 if (program != null) {
82 programEnum.setId(program.getId());
83 } else {
84
85 log.warn("Missing program with label=" + programEnum.name());
86 }
87 } catch(Throwable t) {
88 log.error(String.format("Could not initialized enumeration for program {%s}: %s", programEnum.name(), t.getMessage()), t);
89 }
90 });
91 }
92
93 @Override
94 public List<ProgramVO> getAll() {
95 CriteriaQuery<Program> query = entityManager.getCriteriaBuilder().createQuery(Program.class);
96 Root<Program> root = query.from(Program.class);
97
98 query.select(root);
99
100 return getEntityManager()
101 .createQuery(query)
102 .getResultStream()
103 .map(this::toProgramVO)
104 .collect(Collectors.toList());
105 }
106
107 @Override
108 public List<ProgramVO> findByFilter(ProgramFilterVO filter, int offset, int size, String sortAttribute, SortDirection sortDirection) {
109 Preconditions.checkNotNull(filter);
110 Preconditions.checkArgument(offset >= 0);
111 Preconditions.checkArgument(size > 0);
112
113 EntityManager entityManager = getEntityManager();
114 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
115 CriteriaQuery<Program> query = builder.createQuery(Program.class);
116 Root<Program> root = query.from(Program.class);
117
118 Join<Program, ProgramProperty> upJ = root.join(Program.Fields.PROPERTIES, JoinType.LEFT);
119
120 ParameterExpression<String> withPropertyParam = builder.parameter(String.class);
121 ParameterExpression<Collection> statusIdsParam = builder.parameter(Collection.class);
122 ParameterExpression<String> searchTextParam = builder.parameter(String.class);
123
124 query.select(root).distinct(true)
125 .where(
126 builder.and(
127
128 builder.or(
129 builder.isNull(withPropertyParam),
130 builder.equal(upJ.get(ProgramProperty.Fields.LABEL), withPropertyParam)
131 ),
132
133 builder.or(
134 builder.isNull(statusIdsParam),
135 root.get(Program.Fields.STATUS).get(IReferentialEntity.Fields.ID).in(statusIdsParam)
136 ),
137
138 builder.or(
139 builder.isNull(searchTextParam),
140 builder.like(builder.upper(root.get(Program.Fields.LABEL)), builder.upper(searchTextParam)),
141 builder.like(builder.upper(root.get(Program.Fields.NAME)), builder.upper(searchTextParam))
142 )
143 ));
144
145 if (StringUtils.isNotBlank(sortAttribute)) {
146 if (sortDirection == SortDirection.ASC) {
147 query.orderBy(builder.asc(root.get(sortAttribute)));
148 } else {
149 query.orderBy(builder.desc(root.get(sortAttribute)));
150 }
151 }
152
153 String searchText = StringUtils.trimToNull(filter.getSearchText());
154 String searchTextAnyMatch = null;
155 if (StringUtils.isNotBlank(searchText)) {
156 searchTextAnyMatch = ("*" + searchText + "*");
157 searchTextAnyMatch = searchTextAnyMatch.replaceAll("[*]+", "*");
158 searchTextAnyMatch = searchTextAnyMatch.replaceAll("[%]", "\\%");
159 searchTextAnyMatch = searchTextAnyMatch.replaceAll("[*]", "%");
160 }
161
162 return entityManager.createQuery(query)
163 .setParameter(withPropertyParam, filter.getWithProperty())
164 .setParameter(statusIdsParam, filter.getStatusIds())
165 .setParameter(searchTextParam, searchTextAnyMatch)
166 .setFirstResult(offset)
167 .setMaxResults(size)
168 .getResultList()
169 .stream()
170 .map(this::toProgramVO)
171 .collect(Collectors.toList());
172 }
173
174 @Override
175 public ProgramVO get(final int id) {
176 return toProgramVO(get(Program.class, id));
177 }
178
179 @Override
180 public ProgramVO getByLabel(String label) {
181 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
182 CriteriaQuery<Program> query = builder.createQuery(Program.class);
183 Root<Program> root = query.from(Program.class);
184
185 ParameterExpression<String> labelParam = builder.parameter(String.class);
186
187 query.select(root)
188 .where(builder.equal(root.get(Program.Fields.LABEL), labelParam));
189
190 TypedQuery<Program> q = getEntityManager().createQuery(query)
191 .setParameter(labelParam, label);
192 try {
193 return toProgramVO(q.getSingleResult());
194 } catch(EmptyResultDataAccessException | NoResultException e) {
195 return null;
196 }
197 }
198
199
200 @Override
201 public ProgramVO="../../../../../../net/sumaris/core/vo/administration/programStrategy/ProgramVO.html#ProgramVO">ProgramVO save(ProgramVO source) {
202 Preconditions.checkNotNull(source);
203 Preconditions.checkNotNull(source.getLabel(), "Missing 'label'");
204 Preconditions.checkNotNull(source.getName(), "Missing 'name'");
205 Preconditions.checkNotNull(source.getStatusId(), "Missing 'statusId'");
206
207 EntityManager entityManager = getEntityManager();
208 Program entity = null;
209 if (source.getId() != null) {
210 entity = get(Program.class, source.getId());
211 }
212 boolean isNew = (entity == null);
213 if (isNew) {
214 entity = new Program();
215 }
216
217
218 if (isNew) {
219
220 if (source.getStatusId() == null) {
221 source.setStatusId(config.getStatusIdTemporary());
222 }
223 }
224
225 else {
226
227
228 checkUpdateDateForUpdate(source, entity);
229
230
231 lockForUpdate(entity, LockModeType.PESSIMISTIC_WRITE);
232 }
233
234 programVOToEntity(source, entity, true);
235
236
237 Timestamp newUpdateDate = getDatabaseCurrentTimestamp();
238 entity.setUpdateDate(newUpdateDate);
239
240
241 if (isNew) {
242
243 entity.setCreationDate(newUpdateDate);
244 source.setCreationDate(newUpdateDate);
245
246 entityManager.persist(entity);
247 source.setId(entity.getId());
248 } else {
249 entityManager.merge(entity);
250 }
251
252 source.setUpdateDate(newUpdateDate);
253
254
255 saveProperties(source.getProperties(), entity, newUpdateDate);
256
257 getEntityManager().flush();
258 getEntityManager().clear();
259
260
261
262
263 return source;
264 }
265
266 @Override
267 public List<ReferentialVO> getGears(int programId) {
268 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
269 CriteriaQuery<Gear> query = builder.createQuery(Gear.class);
270 Root<Gear> root = query.from(Gear.class);
271
272 ParameterExpression<Integer> programIdParam = builder.parameter(Integer.class);
273
274 Join<Gear, Strategy> gearInnerJoin = root.joinList(Gear.Fields.STRATEGIES, JoinType.INNER);
275
276 query.select(root)
277 .where(
278 builder.and(
279
280 builder.equal(gearInnerJoin.get(Strategy.Fields.PROGRAM).get(Program.Fields.ID), programIdParam),
281
282 builder.in(root.get(Gear.Fields.STATUS).get(Status.Fields.ID)).value(ImmutableList.of(StatusEnum.ENABLE.getId(), StatusEnum.TEMPORARY.getId()))
283 ));
284
285
286 query.orderBy(builder.asc(root.get(Gear.Fields.LABEL)));
287
288 return getEntityManager()
289 .createQuery(query)
290 .setParameter(programIdParam, programId)
291 .getResultStream()
292 .map(referentialDao::toReferentialVO)
293 .collect(Collectors.toList());
294 }
295
296 @Override
297 public List<TaxonGroupVO> getTaxonGroups(int programId) {
298 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
299 CriteriaQuery<TaxonGroup> query = builder.createQuery(TaxonGroup.class);
300 Root<TaxonGroup> root = query.from(TaxonGroup.class);
301
302 ParameterExpression<Integer> programIdParam = builder.parameter(Integer.class);
303
304 Join<TaxonGroup, TaxonGroupStrategy> innerJoinTGS = root.joinList(TaxonGroup.Fields.STRATEGIES, JoinType.INNER);
305 Join<TaxonGroupStrategy, Strategy> innerJoinS = innerJoinTGS.join(TaxonGroupStrategy.Fields.STRATEGY, JoinType.INNER);
306
307
308 query.select(root)
309 .where(
310 builder.and(
311
312 builder.equal(innerJoinS.get(Strategy.Fields.PROGRAM).get(Program.Fields.ID), programIdParam),
313
314 builder.in(root.get(TaxonGroup.Fields.STATUS).get(Status.Fields.ID)).value(ImmutableList.of(StatusEnum.ENABLE.getId(), StatusEnum.TEMPORARY.getId()))
315 ));
316
317
318 query.orderBy(builder.asc(root.get(TaxonGroup.Fields.LABEL)));
319
320 return getEntityManager()
321 .createQuery(query)
322 .setParameter(programIdParam, programId)
323 .getResultStream()
324 .map(taxonGroupRepository::toTaxonGroupVO)
325 .collect(Collectors.toList());
326 }
327
328 @Override
329 public void delete(int id) {
330 log.debug(String.format("Deleting program {id=%s}...", id));
331 delete(Program.class, id);
332 }
333
334
335 @Override
336 public ProgramVO toProgramVO(Program source) {
337 return toProgramVO(source, ProgramFetchOptions.builder()
338 .withProperties(true)
339 .build());
340 }
341
342 @Override
343 public ProgramVO toProgramVO(Program source, ProgramFetchOptions fetchOptions) {
344 if (source == null) return null;
345
346 ProgramVOistration/programStrategy/ProgramVO.html#ProgramVO">ProgramVO target = new ProgramVO();
347
348 Beans.copyProperties(source, target);
349
350
351 target.setStatusId(source.getStatus().getId());
352
353
354 if (fetchOptions.isWithProperties()) {
355 Map<String, String> properties = Maps.newHashMap();
356 Beans.getStream(source.getProperties())
357 .filter(prop -> Objects.nonNull(prop)
358 && Objects.nonNull(prop.getLabel())
359 && Objects.nonNull(prop.getName())
360 )
361 .forEach(prop -> {
362 if (properties.containsKey(prop.getLabel())) {
363 logger.warn(String.format("Duplicate program property with label {%s}. Overriding existing value with {%s}", prop.getLabel(), prop.getName()));
364 }
365 properties.put(prop.getLabel(), prop.getName());
366 });
367 target.setProperties(properties);
368 }
369
370 return target;
371 }
372
373
374
375
376
377
378 protected void programVOToEntity(ProgramVO source, Program target, boolean copyIfNull) {
379
380 Beans.copyProperties(source, target);
381
382
383 if (copyIfNull || source.getStatusId() != null) {
384 if (source.getStatusId() == null) {
385 target.setStatus(null);
386 }
387 else {
388 target.setStatus(load(Status.class, source.getStatusId()));
389 }
390 }
391
392 }
393
394 protected void saveProperties(Map<String, String> source, Program parent, Timestamp updateDate) {
395 final EntityManager em = getEntityManager();
396 if (MapUtils.isEmpty(source)) {
397 if (parent.getProperties() != null) {
398 List<ProgramProperty> toRemove = ImmutableList.copyOf(parent.getProperties());
399 parent.getProperties().clear();
400 toRemove.forEach(em::remove);
401 }
402 }
403 else {
404 Map<String, ProgramProperty> existingProperties = Beans.splitByProperty(
405 Beans.getList(parent.getProperties()),
406 ProgramProperty.Fields.LABEL);
407 final Status enableStatus = em.getReference(Status.class, StatusEnum.ENABLE.getId());
408 if (parent.getProperties() == null) {
409 parent.setProperties(Lists.newArrayList());
410 }
411 final List<ProgramProperty> targetProperties = parent.getProperties();
412
413
414 source.entrySet().stream()
415 .filter(e -> Objects.nonNull(e.getKey())
416 && Objects.nonNull(e.getValue())
417 )
418 .map(e -> {
419 ProgramProperty prop = existingProperties.remove(e.getKey());
420 boolean isNew = (prop == null);
421 if (isNew) {
422 prop = new ProgramProperty();
423 prop.setLabel(e.getKey());
424 prop.setProgram(parent);
425 prop.setCreationDate(updateDate);
426 }
427 prop.setName(e.getValue());
428 prop.setStatus(enableStatus);
429 prop.setUpdateDate(updateDate);
430 if (isNew) {
431 em.persist(prop);
432 }
433 else {
434 em.merge(prop);
435 }
436 return prop;
437 })
438 .forEach(targetProperties::add);
439
440
441 if (MapUtils.isNotEmpty(existingProperties)) {
442 parent.getProperties().removeAll(existingProperties.values());
443 existingProperties.values().forEach(em::remove);
444 }
445
446 }
447 }
448 }