Skip to content

Package: JpaSpecificationFinder

JpaSpecificationFinder

nameinstructionbranchcomplexitylinemethod
JpaSpecificationFinder(JpaSpecificationExecutor, Function)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
JpaSpecificationFinder(JpaSpecificationFinder, Object)
M: 0 C: 23
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 6
100%
M: 0 C: 1
100%
by(Enum)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
composeSpecification(Root, CriteriaBuilder, List)
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
computeNeededResults()
M: 0 C: 49
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 9
100%
M: 0 C: 1
100%
getSpecification()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$getSpecification$518315e9$1(Root, CriteriaQuery, CriteriaBuilder)
M: 0 C: 17
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
sort(Finder.SortCriterion, Finder.SortDirection)
M: 5 C: 26
84%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 3
75%
M: 0 C: 1
100%
static {...}
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /*
2: * *********************************************************************************************************************
3: *
4: * SolidBlue 3: Data safety
5: * http://tidalwave.it/projects/solidblue3
6: *
7: * Copyright (C) 2023 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/solidblue3j-src
23: * git clone https://github.com/tidalwave-it/solidblue3j-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.util.spring.jpa;
28:
29: import jakarta.annotation.Nonnull;
30: import java.util.ArrayList;
31: import java.util.List;
32: import java.util.function.Function;
33: import java.io.Serial;
34: import jakarta.persistence.criteria.CriteriaBuilder;
35: import jakarta.persistence.criteria.Predicate;
36: import jakarta.persistence.criteria.Root;
37: import org.springframework.data.domain.PageRequest;
38: import org.springframework.data.domain.Sort;
39: import org.springframework.data.jpa.domain.Specification;
40: import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
41: import it.tidalwave.util.Finder;
42: import it.tidalwave.util.spi.HierarchicFinderSupport;
43: import lombok.AllArgsConstructor;
44: import lombok.SneakyThrows;
45: import lombok.extern.slf4j.Slf4j;
46: import static it.tidalwave.util.CollectionUtils.concat;
47: import static lombok.AccessLevel.PRIVATE;
48:
49: /***********************************************************************************************************************
50: *
51: * A {@link Finder} that works with a repository extending {@link JpaSpecificationExecutor}.
52: *
53: * @param <M> the static type of the model object
54: * @param <E> the static type of the JPA entity
55: * @param <F> the static type of the {@code Finder}
56: * @param <R> the static type of the repository
57: * @stereotype Finder
58: * @author Fabrizio Giudici
59: *
60: **********************************************************************************************************************/
61: @AllArgsConstructor(access = PRIVATE) @Slf4j
62: public class JpaSpecificationFinder<M, E, F extends Finder<M>, R extends JpaSpecificationExecutor<E>>
63: extends HierarchicFinderSupport<M, F>
64: {
65: @Serial private static final long serialVersionUID = 0L;
66:
67: @Nonnull
68: protected final R repository;
69:
70: @Nonnull
71: protected final Function<E, M> entityToModel;
72:
73: @Nonnull
74: protected final List<JpaSorter> sorters;
75:
76: /*******************************************************************************************************************
77: *
78: ******************************************************************************************************************/
79: private record JpaSortCriterion (@Nonnull Enum<?> sortingKey) implements SortCriterion
80: {
81: }
82:
83: /*******************************************************************************************************************
84: *
85: ******************************************************************************************************************/
86: public record JpaSorter (@Nonnull JpaSortCriterion criterion, @Nonnull SortDirection direction)
87: {
88: @Nonnull
89: public Sort.Order toOrder()
90: {
91: return new Sort.Order(direction == SortDirection.ASCENDING ? Sort.Direction.ASC : Sort.Direction.DESC,
92: getName(criterion.sortingKey));
93: }
94:
95: @Override @Nonnull
96: public String toString()
97: {
98: return "JpaSorter(%s, %s)".formatted(getName(criterion.sortingKey), direction.name());
99: }
100:
101: @Nonnull @SneakyThrows
102: private static String getName (@Nonnull final Enum<?> sortingKey)
103: {
104: return (String)sortingKey.getClass().getMethod("getName").invoke(sortingKey);
105: }
106: }
107:
108: /*******************************************************************************************************************
109: *
110: * Creates a new instance given a repository and a model-to-entity transformer.
111: *
112: * @param repository the repository
113: * @param entityToModel the transformer
114: *
115: ******************************************************************************************************************/
116: public JpaSpecificationFinder (@Nonnull final R repository, @Nonnull final Function<E, M> entityToModel)
117: {
118: this(repository, entityToModel, List.of());
119: }
120:
121: /*******************************************************************************************************************
122: *
123: * The required constructor for subclasses of {@link HierarchicFinderSupport}.
124: *
125: ******************************************************************************************************************/
126: @SuppressWarnings("unchecked")
127: public JpaSpecificationFinder (@Nonnull final JpaSpecificationFinder<M, E, F, R> other, @Nonnull final Object override)
128: {
129: super(other, override);
130: final var source = getSource(JpaSpecificationFinder.class, other, override);
131: this.repository = (R)source.repository; // See https://stackoverflow.com/questions/76129388
132: this.entityToModel = source.entityToModel;
133: this.sorters = source.sorters;
134: }
135:
136: /*******************************************************************************************************************
137: * {@inheritDoc}
138: ******************************************************************************************************************/
139: @Override @Nonnull
140: public F sort (@Nonnull final SortCriterion criterion, @Nonnull final SortDirection direction)
141: {
142:• if (criterion instanceof final JpaSortCriterion jpaSortCriterion)
143: {
144: final var sorters = concat(this.sorters, new JpaSorter(jpaSortCriterion, direction));
145: return clonedWith(new JpaSpecificationFinder<>(repository, entityToModel, sorters));
146: }
147:
148: return super.sort(criterion, direction);
149: }
150:
151: /*******************************************************************************************************************
152: *
153: * Creates a {@link SortCriterion} by key.
154: *
155: * @param sortingKey the key
156: * @return the {@code SortCriterion}
157: *
158: ******************************************************************************************************************/
159: @Nonnull
160: public static SortCriterion by (@Nonnull final Enum<?> sortingKey)
161: {
162: return new JpaSortCriterion(sortingKey);
163: }
164:
165: /*******************************************************************************************************************
166: * {@inheritDoc}
167: ******************************************************************************************************************/
168: @Override @Nonnull
169: protected final List<M> computeNeededResults()
170: {
171: final var baseTime = System.currentTimeMillis();
172: final var specification = getSpecification();
173: final var pageRequest = PageRequest.of(firstResult, maxResults,
174: Sort.by(sorters.stream().map(JpaSorter::toOrder).toList()));
175: log.info("computeNeededResults() - {}", pageRequest);
176: final var result = repository.findAll(specification, pageRequest).stream().map(entityToModel).toList();
177: log.info(">>>> returning {} items in {} msec", result.size(), System.currentTimeMillis() - baseTime);
178: log.trace(">>>> returning {}", result);
179: return result;
180: }
181:
182: /*******************************************************************************************************************
183: *
184: ******************************************************************************************************************/
185: @Nonnull
186: protected Specification<E> getSpecification()
187: {
188: return (root, query, criteriaBuilder) ->
189: {
190: final var predicates = new ArrayList<Predicate>();
191: composeSpecification(root, criteriaBuilder, predicates);
192: return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
193: };
194: }
195:
196: /*******************************************************************************************************************
197: *
198: ******************************************************************************************************************/
199: protected void composeSpecification (@Nonnull final Root<E> root,
200: @Nonnull final CriteriaBuilder criteriaBuilder,
201: @Nonnull final List<? super Predicate> predicates)
202: {
203: }
204: }