Skip to content

Method: processSql(String, Finder.SortDirection)

1: /*
2: * *********************************************************************************************************************
3: *
4: * TheseFoolishThings: Miscellaneous utilities
5: * http://tidalwave.it/projects/thesefoolishthings
6: *
7: * Copyright (C) 2009 - 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/thesefoolishthings-src
23: * git clone https://github.com/tidalwave-it/thesefoolishthings-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.thesefoolishthings.examples.jpafinderexample.impl;
28:
29: import javax.annotation.Nonnegative;
30: import javax.annotation.Nonnull;
31: import java.util.ArrayList;
32: import java.util.List;
33: import java.util.Optional;
34: import java.util.concurrent.atomic.AtomicReference;
35: import java.util.function.Function;
36: import java.util.stream.Collectors;
37: import javax.persistence.EntityManager;
38: import javax.persistence.TypedQuery;
39: import it.tidalwave.util.Finder;
40: import it.tidalwave.util.Pair;
41: import it.tidalwave.thesefoolishthings.examples.jpafinderexample.TxManager;
42: import lombok.AccessLevel;
43: import lombok.AllArgsConstructor;
44: import lombok.RequiredArgsConstructor;
45: import lombok.extern.slf4j.Slf4j;
46: import static it.tidalwave.util.CollectionUtils.concat;
47:
48: /***********************************************************************************************************************
49: *
50: * @author Fabrizio Giudici
51: *
52: **********************************************************************************************************************/
53: // START SNIPPET: JPAExampleFinder
54: @AllArgsConstructor(access = AccessLevel.PRIVATE) @Slf4j
55: public class JpaFinder<T, E> implements Finder<T>
56: {
57: // START SNIPPET: sort-criterion
58: @RequiredArgsConstructor
59: static final class JpaqlSortCriterion implements SortCriterion
60: {
61: @Nonnull
62: private final String field;
63:
64: @Nonnull
65: public String processSql (@Nonnull final String jpaql, @Nonnull final SortDirection sortDirection)
66: {
67:• final String orderBy = jpaql.contains("ORDER BY") ? ", " : " ORDER BY ";
68:• return jpaql + orderBy + field + ((sortDirection == SortDirection.DESCENDING) ? " DESC" : "");
69: }
70: }
71:
72: public static final SortCriterion BY_FIRST_NAME = new JpaqlSortCriterion("p.firstName");
73: public static final SortCriterion BY_LAST_NAME = new JpaqlSortCriterion("p.lastName");
74: // END SNIPPET: sort-criterion
75:
76: // START SNIPPET: fields
77: @Nonnull
78: private final Class<E> entityClass;
79:
80: @Nonnull
81: private final Function<E, T> fromEntity;
82:
83: @Nonnull
84: private final TxManager txManager;
85:
86: @Nonnegative
87: private final int firstResult;
88:
89: @Nonnegative
90: private final int maxResults;
91:
92: @Nonnull
93: private final List<Pair<JpaqlSortCriterion, SortDirection>> sortCriteria;
94: // END SNIPPET: fields
95:
96: public JpaFinder (@Nonnull final Class<E> entityClass,
97: @Nonnull final Function<E, T> fromEntity,
98: @Nonnull final TxManager txManager)
99: {
100: this(entityClass, fromEntity, txManager, 0, Integer.MAX_VALUE, new ArrayList<>());
101: }
102:
103: // START SNIPPET: intermediate-methods
104: @Override @Nonnull
105: public Finder<T> from (@Nonnegative final int firstResult)
106: {
107: return new JpaFinder(entityClass, fromEntity, txManager, firstResult, maxResults, sortCriteria);
108: }
109:
110: @Override @Nonnull
111: public Finder<T> max (@Nonnegative final int maxResults)
112: {
113: return new JpaFinder(entityClass, fromEntity, txManager, firstResult, maxResults, sortCriteria);
114: }
115: // END SNIPPET: intermediate-methods
116:
117: // START SNIPPET: sort-method
118: @Override @Nonnull
119: public Finder<T> sort (@Nonnull final SortCriterion criterion, @Nonnull final SortDirection direction)
120: {
121: if (!(criterion instanceof JpaqlSortCriterion))
122: {
123: throw new IllegalArgumentException("Can't sort by " + criterion);
124: }
125:
126: return new JpaFinder(entityClass,
127: fromEntity,
128: txManager,
129: firstResult,
130: maxResults,
131: concat(sortCriteria, Pair.of((JpaqlSortCriterion)criterion, direction)));
132: }
133: // END SNIPPET: sort-method
134:
135: // START SNIPPET: termination-methods
136: @Override @Nonnull
137: public Optional<T> optionalResult()
138: {
139: final List<? extends T> results = results();
140:
141: if (results.size() > 1)
142: {
143: throw new RuntimeException("More than a single result");
144: }
145:
146: return (Optional<T>)results.stream().findFirst();
147: }
148:
149: @Override @Nonnull
150: public Optional<T> optionalFirstResult()
151: {
152: // Warning: the stream must be consumed *within* runInTx2()
153: return txManager.computeInTx(em -> createQuery(em, entityClass, "SELECT p")
154: .getResultStream()
155: .findFirst()
156: .map(fromEntity));
157: }
158:
159: @Override @Nonnull
160: public List<? extends T> results()
161: {
162: // Warning: the stream must be consumed *within* runInTx2()
163: return txManager.computeInTx(em -> createQuery(em, entityClass, "SELECT p")
164: .getResultStream()
165: .map(fromEntity)
166: .collect(Collectors.toList()));
167: }
168:
169: @Override @Nonnegative
170: public int count()
171: {
172: return txManager.computeInTx(em -> createQuery(em, Long.class, "SELECT COUNT(p)").getSingleResult()).intValue();
173: }
174: // END SNIPPET: termination-methods
175:
176: // START SNIPPET: createQueryFull
177: @Nonnull
178: private <R> TypedQuery<R> createQuery (@Nonnull final EntityManager em,
179: @Nonnull final Class<R> resultType,
180: @Nonnull final String jpaqlPrefix)
181: {
182: final AtomicReference<String> buffer =
183: new AtomicReference<>(jpaqlPrefix + " FROM " + entityClass.getSimpleName() + " p");
184: sortCriteria.forEach(p -> buffer.updateAndGet(prev -> p.a.processSql(prev, p.b)));
185: final String jpaql = buffer.get();
186: log.info(">>>> {}", jpaql);
187: // START SNIPPET: createQuery
188: return em.createQuery(jpaql, resultType).setFirstResult(firstResult).setMaxResults(maxResults);
189: // END SNIPPET: createQuery
190: }
191: // END SNIPPET: createQueryFull
192: }
193: // END SNIPPET: JPAExampleFinder