Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
Criteria |
|
| 2.671232876712329;2.671 | ||||
Criteria$1 |
|
| 2.671232876712329;2.671 |
1 | /* | |
2 | * Copyright 2006-2011 The Kuali Foundation | |
3 | * | |
4 | * Licensed under the Educational Community License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.opensource.org/licenses/ecl2.php | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.kuali.rice.core.framework.persistence.jpa.criteria; | |
17 | ||
18 | import org.kuali.rice.core.api.util.Truth; | |
19 | import org.kuali.rice.core.api.util.type.TypeUtils; | |
20 | ||
21 | import javax.persistence.Query; | |
22 | import java.util.ArrayList; | |
23 | import java.util.Arrays; | |
24 | import java.util.Collection; | |
25 | import java.util.HashMap; | |
26 | import java.util.Iterator; | |
27 | import java.util.LinkedHashMap; | |
28 | import java.util.List; | |
29 | import java.util.Map; | |
30 | import java.util.regex.Pattern; | |
31 | ||
32 | /** | |
33 | * A criteria builder for JPQL Query objects. | |
34 | * | |
35 | * <p>All entities in queries generated by this class will be given an alias, and an initial entity (with an initial alias) | |
36 | * is mandatory. If no alias is given or if a constructor without an alias parameter is used, a new alias name will be | |
37 | * auto-generated. | |
38 | * | |
39 | * <p>The interpretation of the initial entity depends on what type of query is intended. If a SELECT query is desired, | |
40 | * the initial entity will be the first to appear in the FROM clause. If a DELETE query is desired, the initial | |
41 | * entity will be the one that appears in the DELETE FROM clause. If an UPDATE query is desired, the initial entity | |
42 | * will be the one that appears in the UPDATE clause. | |
43 | * | |
44 | * <p>Most of the methods of this class rely on String expressions representing entity properties, or functions | |
45 | * acting on constants or entity properties. If the expression contains functions or if | |
46 | * you wish to reference the property of an entity other than the first aliased entity of the query, then the | |
47 | * String expression must include the alias of the entity in the format __JPA_ALIAS[[index]]__ or the format | |
48 | * __JPA_ALIAS[['alias']]__ , where "index" is a numeric index pointing to the Nth alias of the current query in the | |
49 | * order that the aliases were added to the query (starting at zero), and where "'alias'" is the String name of the | |
50 | * entity alias enclosed in single quotes. | |
51 | * | |
52 | * <p>If the String expression as defined above does not contain custom JPA aliases, then the initial alias of the current | |
53 | * Criteria (and a '.' to separate the alias from the expression) will be prepended to the expression automatically. | |
54 | * | |
55 | * <p>If the current Criteria is intended for use as a sub-query, then aliases of the parent Criteria can be referred to | |
56 | * by name (so long as they do not conflict with the alias names in the sub-Criteria) or by specifying the bitwise NOT of | |
57 | * the alias index in the parent Criteria (-1 for 0, -2 for 1, etc.). When including a sub-query in a parent query, the | |
58 | * parent Criteria will handle the updating of the alias indexes and will auto-generate new aliases as needed if any of | |
59 | * the sub-query's defined aliases are duplicates of those in the parent query. | |
60 | * | |
61 | * <p>For Criteria objects intended to be ANDed or ORed with an existing Criteria, it is assumed that any named or indexed | |
62 | * aliases refer to exactly the same aliases as in the existing Criteria. Any auto-prepended aliases as described above | |
63 | * will always be referred to by index, so it is safe for them to be included in the Criteria to be ANDed/ORed with the | |
64 | * existing one. Both of these details also apply when adding new Criteria to the HAVING clause of an existing Criteria, or | |
65 | * when inserting the negation of another Criteria's conditions into an existing Criteria as a "NOT (...)" expression. | |
66 | * | |
67 | * <p>Also, during query construction, the Criteria API will automatically include substrings in the format | |
68 | * __JPA_PARAM[['parameter']]__ when adding input parameters, where 'parameter' is the auto-generated name of the | |
69 | * parameter. Care should be taken to ensure that __JPA_ALIAS[[...]]__ and __JPA_PARAM[[...]]__ expressions are not | |
70 | * being added to queries that wish to interpret such String patterns literally. | |
71 | * | |
72 | * <p>Note that Criteria instances are not thread-safe, so external synchronization is necessary if operating on them | |
73 | * from multiple threads. | |
74 | * | |
75 | * <p>TODO: Verify if any other features need to be added to this API. | |
76 | * | |
77 | * @author Kuali Rice Team (rice.collab@kuali.org) | |
78 | */ | |
79 | @SuppressWarnings("unchecked") | |
80 | public class Criteria { | |
81 | ||
82 | 0 | private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(Criteria.class); |
83 | 0 | private static final Pattern APOS_PAT = Pattern.compile("'"); |
84 | 0 | private static final String[] LOOKUP_WILDCARDS = {"*", "?"}; |
85 | 0 | private static final String[] ESCAPED_LOOKUP_WILDCARDS = {"\\*", "\\?"}; |
86 | 0 | private static final char[] JPQL_WILDCARDS = {'%', '_'}; |
87 | ||
88 | /** The String representing the beginning of a by-name or by-index reference to an entity alias. */ | |
89 | public static final String JPA_ALIAS_PREFIX = "__JPA_ALIAS[["; | |
90 | /** The String representing the beginning of a by-name reference to an input parameter. */ | |
91 | public static final String JPA_PARAM_PREFIX = "__JPA_PARAM[["; | |
92 | /** The String representing the termination of by-name/index references to aliases or input parameters. */ | |
93 | public static final String JPA_ALIAS_SUFFIX = "]]__"; | |
94 | ||
95 | private static final String JPA_INITIAL_ALIAS = JPA_ALIAS_PREFIX + "0" + JPA_ALIAS_SUFFIX; | |
96 | private static final String JPA_PARENT_INITIAL_ALIAS = JPA_ALIAS_PREFIX + "-1" + JPA_ALIAS_SUFFIX; | |
97 | ||
98 | private static final String JPA_PARAM_PREFIX_WITH_COLON = ":" + JPA_PARAM_PREFIX; | |
99 | 0 | private static final int ALIAS_PREFIX_LEN = JPA_ALIAS_PREFIX.length(); |
100 | 0 | private static final int PARAM_PREFIX_LEN = JPA_PARAM_PREFIX.length(); |
101 | 0 | private static final int ALIAS_SUFFIX_LEN = JPA_ALIAS_SUFFIX.length(); |
102 | ||
103 | private Integer searchLimit; | |
104 | ||
105 | private int generatedAliasCount; | |
106 | ||
107 | private int generatedBindParamCount; | |
108 | ||
109 | 0 | private boolean distinct = false; |
110 | ||
111 | 0 | private final List<String> entityAliases = new ArrayList<String>(); |
112 | ||
113 | 0 | private final List<String> indexedAliasPlaceholders = new ArrayList<String>(); |
114 | ||
115 | 0 | private final List<String> namedAliasPlaceholders = new ArrayList<String>(); |
116 | ||
117 | 0 | private final Map<String,Integer> aliasIndexes = new HashMap<String,Integer>(); |
118 | ||
119 | 0 | private final StringBuilder selectClause = new StringBuilder(); |
120 | ||
121 | 0 | private final StringBuilder fromClause = new StringBuilder(); |
122 | ||
123 | 0 | private final StringBuilder whereClause = new StringBuilder(50); |
124 | ||
125 | 0 | private final StringBuilder groupByClause = new StringBuilder(); |
126 | ||
127 | 0 | private final StringBuilder havingClause = new StringBuilder(); |
128 | ||
129 | 0 | private final StringBuilder orderByClause = new StringBuilder(); |
130 | ||
131 | 0 | private final StringBuilder setClause = new StringBuilder(); |
132 | ||
133 | private final String initialEntityName; | |
134 | ||
135 | 0 | protected Map<String, Object> params = new LinkedHashMap<String, Object>(); |
136 | ||
137 | /** | |
138 | * Constructs a new Criteria instance that includes the given initial entity. An alias will be | |
139 | * auto-generated for this entity, and the entity's alias will be included in the SELECT clause if this | |
140 | * Criteria is intended to be used as a SELECT query. | |
141 | * | |
142 | * @param entityName The class name of the initial JPA entity. | |
143 | */ | |
144 | public Criteria(String entityName) { | |
145 | 0 | this(entityName, "a", true); |
146 | 0 | } |
147 | ||
148 | /** | |
149 | * Constructs a new Criteria instance that includes the given initial entity. The given alias | |
150 | * will be used for this entity if it is non-null, and the alias will be included in the SELECT clause | |
151 | * if this Criteria is intended to be used as a SELECT query. | |
152 | * | |
153 | * @param entityName The class name of the initial JPA entity. | |
154 | * @param alias The alias to use for this entity; if it is null, a new alias will be auto-generated. | |
155 | */ | |
156 | public Criteria(String entityName, String alias) { | |
157 | 0 | this(entityName, alias, true); |
158 | 0 | } |
159 | ||
160 | /** | |
161 | * Constructs a new Criteria instance that includes the given initial entity. An alias will be | |
162 | * auto-generated for this entity, and the extra parameter indicates whether this alias should also be | |
163 | * added to the query's SELECT clause (if this Criteria is intended to be used as a SELECT query). | |
164 | * | |
165 | * @param entityName The class name of the initial JPA entity. | |
166 | * @param includeEntityInSelect Indicates whether this entity's alias should be added to the SELECT clause. | |
167 | */ | |
168 | public Criteria(String entityName, boolean includeEntityInSelect) { | |
169 | 0 | this(entityName, "a", includeEntityInSelect); |
170 | 0 | } |
171 | ||
172 | /** | |
173 | * Constructs a new Criteria instance that includes the given initial entity. The given alias | |
174 | * will be used for this entity if it is non-null, and the extra parameter indicates whether the alias | |
175 | * should be added to the SELECT clause (if this Criteria is intended to be used as a SELECT query). | |
176 | * | |
177 | * @param entityName The class name of the initial JPA entity. | |
178 | * @param alias The alias to use for this entity; if it is null, a new alias will be auto-generated. | |
179 | * @param includeEntityAliasInSelect Indicates whether this entity's alias should be added to the SELECT clause. | |
180 | */ | |
181 | 0 | public Criteria(String entityName, String alias, boolean includeEntityAliasInSelect) { |
182 | 0 | this.initialEntityName = entityName; |
183 | 0 | from(entityName, alias, includeEntityAliasInSelect); |
184 | 0 | } |
185 | ||
186 | /* | |
187 | * Adds the given alias to the Criteria's alias lists/maps. If the alias is null or it duplicates an | |
188 | * existing one, a new alias will be auto-generated. | |
189 | * | |
190 | * Returns the new name of the alias, or the passed-in name if non-null and no conflicts were found. | |
191 | */ | |
192 | private String addAlias(String alias) { | |
193 | 0 | if (alias == null) { |
194 | 0 | alias = "a" + (generatedAliasCount++); |
195 | } | |
196 | 0 | while (aliasIndexes.containsKey(alias)) { |
197 | 0 | alias = "a" + (generatedAliasCount++); |
198 | } | |
199 | 0 | entityAliases.add(alias); |
200 | 0 | aliasIndexes.put(alias, entityAliases.size() - 1); |
201 | 0 | indexedAliasPlaceholders.add(new StringBuilder(30).append(JPA_ALIAS_PREFIX).append(indexedAliasPlaceholders.size()).append(JPA_ALIAS_SUFFIX).toString()); |
202 | 0 | namedAliasPlaceholders.add(new StringBuilder(30).append(JPA_ALIAS_PREFIX).append('\'').append(alias).append('\'').append(JPA_ALIAS_SUFFIX).toString()); |
203 | 0 | return alias; |
204 | } | |
205 | ||
206 | /** | |
207 | * Adds the given expression to the SELECT clause. If no properly-referenced alias appears | |
208 | * in the String expression, it will be assumed that the expression is accessing something | |
209 | * on or through the initial entity. See the description of this class for more information | |
210 | * on how String expressions are interpreted. | |
211 | * | |
212 | * @param resultExpression The String expression to add to the SELECT clause. | |
213 | */ | |
214 | public void select(String resultExpression) { | |
215 | 0 | if (resultExpression.contains(JPA_ALIAS_PREFIX)) { |
216 | 0 | selectClause.append((selectClause.length() > 0) ? ", " : "").append(resultExpression); |
217 | } else { | |
218 | 0 | selectClause.append((selectClause.length() > 0) ? ", " : "").append(JPA_INITIAL_ALIAS).append('.').append(resultExpression); |
219 | } | |
220 | 0 | } |
221 | ||
222 | /** | |
223 | * Adds a new JPA entity to the FROM clause. The entity will be given the provided alias if it | |
224 | * is non-null and does not conflict with any existing alias names; otherwise, a new alias name | |
225 | * will be auto-generated. An extra parameter is also included for indicating whether this | |
226 | * Criteria should automatically include the entity's alias in the SELECT clause. | |
227 | * | |
228 | * @param entityName The class name of the new entity. | |
229 | * @param alias The alias to use for this entity. | |
230 | * @param includeEntityAliasInSelect Indicates whether to include the entity's alias in the SELECT clause. | |
231 | * @return The provided alias if it's non-null and does not conflict with any existing names, or an auto-generated alias name otherwise. | |
232 | */ | |
233 | public String from(String entityName, String alias, boolean includeEntityAliasInSelect) { | |
234 | 0 | alias = addAlias(alias); |
235 | 0 | if (includeEntityAliasInSelect) { |
236 | 0 | select(namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); |
237 | } | |
238 | 0 | fromClause.append((fromClause.length() > 0) ? ", " : " FROM ").append(entityName).append(" AS ").append( |
239 | namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); | |
240 | 0 | return alias; |
241 | } | |
242 | ||
243 | /** | |
244 | * Adds a new "IN (...)" expression containing the given collection expression into the FROM clause. | |
245 | * The collection will be given the provided alias if it is non-null and does not conflict with | |
246 | * any existing alias names; otherwise, a new alias name will be auto-generated. An extra parameter | |
247 | * is also included for indicating whether this Criteria should automatically include the | |
248 | * collection's alias in the SELECT clause. | |
249 | * | |
250 | * <p>If no properly-referenced alias appears in the String expression, it will be assumed that the | |
251 | * expression points to a collection that is accessible on or through the initial entity. See the | |
252 | * description of this class for more information on how String expressions are interpreted. | |
253 | * | |
254 | * @param collectionName A String expression that points to an entity collection. | |
255 | * @param alias The alias to use for this collection expression. | |
256 | * @param includeCollectionAliasInSelect Indicates whether to include the collection's alias in the SELECT clause. | |
257 | * @return The provided alias if it's non-null and does not conflict with any existing names, or an auto-generated alias name otherwise. | |
258 | */ | |
259 | public String fromIn(String collectionName, String alias, boolean includeCollectionAliasInSelect) { | |
260 | 0 | alias = addAlias(alias); |
261 | 0 | if (includeCollectionAliasInSelect) { |
262 | 0 | select(namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); |
263 | } | |
264 | // The optional "AS" keyword is excluded when creating the "IN (...)" expression because Hibernate will throw an exception if it exists. | |
265 | 0 | if (collectionName.contains(JPA_ALIAS_PREFIX)) { |
266 | 0 | fromClause.append(", IN (").append(collectionName).append(") ").append(namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); |
267 | } else { | |
268 | 0 | fromClause.append(", IN (").append(JPA_INITIAL_ALIAS).append('.').append(collectionName).append(") ").append( |
269 | namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); | |
270 | } | |
271 | 0 | return alias; |
272 | } | |
273 | ||
274 | /** | |
275 | * Adds a new JOIN to the most-recently-added entity in the FROM clause. The association | |
276 | * will be given the provided alias if it is non-null and does not conflict with | |
277 | * any existing alias names; otherwise, a new alias name will be auto-generated. An extra parameter | |
278 | * is also included for indicating whether this Criteria should automatically include the | |
279 | * association's alias in the SELECT clause. | |
280 | * | |
281 | * <p>If no properly-referenced alias appears in the String expression, it will be assumed that the | |
282 | * expression points to an association that is accessible on or through the initial entity. See the | |
283 | * description of this class for more information on how String expressions are interpreted. | |
284 | * | |
285 | * @param associationName A String expression that points to an entity association. | |
286 | * @param alias The alias to use for this association expression. | |
287 | * @param includeAssociationAliasInSelect Indicates whether to include the collection's alias in the SELECT clause. | |
288 | * @param innerJoin If true, the join will be an inner join; otherwise, it will be a left (outer) join. | |
289 | * @return The provided alias if it's non-null and does not conflict with any existing names, or an auto-generated alias name otherwise. | |
290 | */ | |
291 | public String join(String associationName, String alias, boolean includeAssociationAliasInSelect, boolean innerJoin) { | |
292 | 0 | alias = addAlias(alias); |
293 | 0 | if (includeAssociationAliasInSelect) { |
294 | 0 | select(namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); |
295 | } | |
296 | 0 | if (associationName.contains(JPA_ALIAS_PREFIX)) { |
297 | 0 | fromClause.append(innerJoin ? " INNER JOIN " : " LEFT JOIN ").append(associationName).append(" AS ").append( |
298 | namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); | |
299 | } else { | |
300 | 0 | fromClause.append(innerJoin ? " INNER JOIN " : " LEFT JOIN ").append(JPA_INITIAL_ALIAS).append('.').append( |
301 | associationName).append(" AS ").append(namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); | |
302 | } | |
303 | 0 | return alias; |
304 | } | |
305 | ||
306 | /** | |
307 | * Adds a new JOIN FETCH to the most-recently-added entity in the FROM clause. If no | |
308 | * properly-referenced alias appears in the String expression, it will be assumed that the | |
309 | * expression points to an association that is accessible on or through the initial entity. See the | |
310 | * description of this class for more information on how String expressions are interpreted. | |
311 | * | |
312 | * @param associationName A String expression that points to an entity association. | |
313 | * @param innerJoin If true, the join will be an inner join; otherwise, it will be a left (outer) join. | |
314 | */ | |
315 | public void joinFetch(String associationName, boolean innerJoin) { | |
316 | 0 | if (associationName.contains(JPA_ALIAS_PREFIX)) { |
317 | 0 | fromClause.append(innerJoin ? " INNER JOIN FETCH " : " LEFT JOIN FETCH ").append(associationName); |
318 | } else { | |
319 | 0 | fromClause.append(innerJoin ? " INNER JOIN FETCH " : " LEFT JOIN FETCH ").append(JPA_INITIAL_ALIAS).append('.').append(associationName); |
320 | } | |
321 | 0 | } |
322 | ||
323 | /** | |
324 | * Adds a new attribute assignment expression to the SET clause (for use with UPDATE queries). It is | |
325 | * assumed that the given attribute belongs to the initial entity, so a custom alias should not be used. | |
326 | * | |
327 | * @param attributeName An expression pointing to an attribute that is accessible on or through the initial entity. | |
328 | * @param value The value to assign to the attribute. | |
329 | */ | |
330 | public void set(String attributeName, Object value) { | |
331 | 0 | setClause.append((setClause.length() > 0) ? ", " : " SET ").append(JPA_INITIAL_ALIAS).append('.').append(attributeName).append( |
332 | " = ").append(addAttr(fixPeriods(attributeName), value)); | |
333 | 0 | } |
334 | ||
335 | /** | |
336 | * Adds one new attribute assignment expression to the SET clause for each entry in the given Map (for use with | |
337 | * UPDATE queries). It is assumed that the attributes belong to the initial entity, so custom aliases should not be used. | |
338 | * | |
339 | * @param attributes A Map containing expressions pointing to initial-entity-accessible attributes and their associated values. | |
340 | */ | |
341 | public void set(Map<String,Object> attributes) { | |
342 | 0 | for (Map.Entry<String,Object> attribute : attributes.entrySet()) { |
343 | 0 | setClause.append((setClause.length() > 0) ? ", " : " SET ").append(JPA_INITIAL_ALIAS).append('.').append(attribute.getKey()).append( |
344 | " = ").append(addAttr(fixPeriods(attribute.getKey()), attribute.getValue())); | |
345 | } | |
346 | 0 | } |
347 | ||
348 | /* | |
349 | * Converts an Object into a JPQL-valid constant, or creates a new input parameter for it, depending on the Object type. | |
350 | * Booleans are converted to "true" or "false", numbers are converted to their String representations, Strings are | |
351 | * surrounded in single quotes and their existing single quotes are doubled, Class objects are converted to their | |
352 | * getName() values and are left unquoted, null values are ignored, and all other Objects are assigned to an input | |
353 | * parameter. The fixed constant or parameter name will be appended to the end of the given StringBuilder. | |
354 | */ | |
355 | private void fixValue(StringBuilder queryClause, Object value) { | |
356 | 0 | if (value == null) { return; } |
357 | 0 | Class<?> propertyType = value.getClass(); |
358 | 0 | if(TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType)) { |
359 | 0 | queryClause.append(value.toString()); |
360 | 0 | } else if (TypeUtils.isStringClass(propertyType)) { |
361 | 0 | queryClause.append('\'').append(fixSingleQuotes(value.toString())).append('\''); |
362 | 0 | } else if (TypeUtils.isBooleanClass(propertyType)) { |
363 | 0 | queryClause.append(value.toString()); |
364 | 0 | } else if (value instanceof Class) { |
365 | 0 | queryClause.append(((Class)value).getName()); |
366 | } else { | |
367 | 0 | queryClause.append(addAttr("bind_param" + (++generatedBindParamCount), value)); |
368 | } | |
369 | 0 | } |
370 | ||
371 | /* | |
372 | * Fixes the single quotes of the given String by doubling them. Note that this method | |
373 | * will *not* wrap the final value in single quotes. | |
374 | */ | |
375 | private String fixSingleQuotes(String value) { | |
376 | 0 | return APOS_PAT.matcher(value).replaceAll("''"); |
377 | } | |
378 | ||
379 | /* | |
380 | * Fixes the periods of the given String by converting them to underscores. | |
381 | */ | |
382 | private String fixPeriods(String value) { | |
383 | 0 | return value.replace('.', '_'); |
384 | } | |
385 | ||
386 | /* | |
387 | * Fixes the search pattern by converting all non-escaped lookup wildcards ("*" and "?") into their | |
388 | * respective JPQL wildcards ("%" and "_"). Any lookup wildcards escaped by a backslash are converted | |
389 | * into their non-backslashed equivalents. | |
390 | */ | |
391 | private String fixSearchPattern(String value) { | |
392 | 0 | StringBuilder fixedPattern = new StringBuilder(value); |
393 | 0 | int valueLen = value.length(); |
394 | String lookupWildcard; | |
395 | String escapedLookupWildcard; | |
396 | char jpqlWildcard; | |
397 | // Convert all non-escaped wildcards. | |
398 | 0 | for (int i = 0; i < LOOKUP_WILDCARDS.length; i++) { |
399 | 0 | lookupWildcard = LOOKUP_WILDCARDS[i]; |
400 | 0 | escapedLookupWildcard = ESCAPED_LOOKUP_WILDCARDS[i]; |
401 | 0 | jpqlWildcard = JPQL_WILDCARDS[i]; |
402 | 0 | int wildcardIndex = fixedPattern.indexOf(lookupWildcard); |
403 | 0 | int escapedWildcardIndex = fixedPattern.indexOf(escapedLookupWildcard); |
404 | 0 | while (wildcardIndex != -1) { |
405 | 0 | if (wildcardIndex == 0 || escapedWildcardIndex != wildcardIndex - 1) { |
406 | 0 | fixedPattern.setCharAt(wildcardIndex, jpqlWildcard); |
407 | 0 | wildcardIndex = fixedPattern.indexOf(lookupWildcard, wildcardIndex); |
408 | } else { | |
409 | 0 | fixedPattern.replace(escapedWildcardIndex, wildcardIndex + 1, lookupWildcard); |
410 | 0 | wildcardIndex = fixedPattern.indexOf(lookupWildcard, wildcardIndex); |
411 | 0 | escapedWildcardIndex = fixedPattern.indexOf(escapedLookupWildcard, wildcardIndex); |
412 | } | |
413 | } | |
414 | } | |
415 | 0 | return fixedPattern.toString(); |
416 | } | |
417 | ||
418 | /* | |
419 | * Automatically appends a new " AND " to the WHERE clause if adding more than one condition to it, | |
420 | * and also appends the initial entity's alias and a '.' if the expression does not contain any | |
421 | * properly-referenced aliases (as defined in the description of this class). | |
422 | */ | |
423 | private void preparePrefixIfNecessary(String attribute) { | |
424 | 0 | whereClause.append((whereClause.length() > 0) ? " AND " : ""); |
425 | 0 | if (!attribute.contains(JPA_ALIAS_PREFIX)) { |
426 | 0 | whereClause.append(JPA_INITIAL_ALIAS).append('.'); |
427 | } | |
428 | 0 | } |
429 | ||
430 | /* | |
431 | * Generates a new input parameter name and associates it with the given value, or (if the value | |
432 | * is a Class object) just returns the value's getName() String w/out adding it to a parameter. The | |
433 | * parameter's name will resemble the "attribute" String with the periods converted to | |
434 | * underscores, unless such a parameter already exists or "attribute" contains a properly-referenced | |
435 | * alias (in which case a new unique name will be auto-generated). | |
436 | * Returns the finalized input parameter name as a "__JPA_PARAM[['...']]__" String (with a ":" prefix) | |
437 | * if the value is not a Class; otherwise, returns the getName() String of the Class object. | |
438 | */ | |
439 | private String prepareAttribute(String attribute, Object value) { | |
440 | 0 | return ( value instanceof Class ? ((Class)value).getName() : |
441 | ( attribute.contains(JPA_ALIAS_PREFIX) ? addAttr("bind_param" + (++generatedBindParamCount), value) : addAttr(fixPeriods(attribute), value) ) ); | |
442 | } | |
443 | ||
444 | /* | |
445 | * Generates new input parameter names using "attribute" as a base, and associates each new | |
446 | * parameter with the corresponding provided value (unless the given value is a Class object, | |
447 | * in which case the Class's getName() String will be used w/out adding it to a parameter). | |
448 | * The parameters' names will resemble the "attribute" String (with the periods converted to | |
449 | * underscores) plus the suffix "_bN" (where N is the index of the value associated with the | |
450 | * parameter), unless such parameters already exist or "attribute" contains a | |
451 | * properly-referenced alias (in which case new unique names will be auto-generated). | |
452 | * Returns an array of parameter names as "__JPA_PARAM[['...']]__" Strings (with ":" prefixes), | |
453 | * where each name corresponds to the associated value at the same index, and where the returned | |
454 | * array has a length equal to the number of provided values (but note that if a given value is a | |
455 | * Class object, its getName() String will be returned at that index instead and no input | |
456 | * parameter will be added for it). | |
457 | */ | |
458 | private String[] prepareAttributes(String attribute, Object... values) { | |
459 | 0 | int count = values.length; |
460 | 0 | String[] fixedAttrs = new String[count]; |
461 | 0 | if (attribute.contains(JPA_ALIAS_PREFIX)) { |
462 | 0 | int tempParamCount = ++generatedBindParamCount; |
463 | 0 | for (int i = 0; i < count; i++) { |
464 | 0 | fixedAttrs[i] = (values[i] instanceof Class) ? ((Class)values[i]).getName() : |
465 | addAttr(new StringBuilder(20).append("bind_param").append(tempParamCount).append("_b").append(i + 1).toString(), values[i]); | |
466 | } | |
467 | 0 | } else { |
468 | 0 | attribute = fixPeriods(attribute); |
469 | 0 | for (int i = 0; i < count; i++) { |
470 | 0 | fixedAttrs[i] = (values[i] instanceof Class) ? ((Class)values[i]).getName() : |
471 | addAttr(new StringBuilder(attribute.length() + 5).append(attribute).append("_b").append(i + 1).toString(), values[i]); | |
472 | } | |
473 | } | |
474 | 0 | return fixedAttrs; |
475 | } | |
476 | ||
477 | /* | |
478 | * Automatically appends a new " AND " to the WHERE clause if adding more than one condition to it, | |
479 | * and also appends the initial entity's alias and a '.' if the expression does not contain any | |
480 | * properly-referenced aliases (as defined in the description of this class). In addition, a new | |
481 | * input parameter name is generated, and the parameter will be associated with the given value (unless | |
482 | * the object is a Class object, in which case its getName() value will be used instead and no input | |
483 | * parameter will be created for it). The parameter's name will resemble the "attribute" String with | |
484 | * the periods converted to underscores, unless such a parameter already exists or "attribute" contains | |
485 | * a properly-referenced alias (in which case a new unique name will be auto-generated). | |
486 | * Returns the finalized input parameter name as a "__JPA_PARAM[['...']]__" String (with a ":" prefix) | |
487 | * if it is not a Class object; otherwise, its getName() String will be returned instead. | |
488 | */ | |
489 | private String preparePrefixAndAttributeIfNecessary(String attribute, Object value) { | |
490 | 0 | whereClause.append((whereClause.length() > 0) ? " AND " : ""); |
491 | 0 | if (value instanceof Class) { |
492 | 0 | return ((Class)value).getName(); |
493 | 0 | } else if (attribute.contains(JPA_ALIAS_PREFIX)) { |
494 | 0 | return addAttr("bind_param" + (++generatedBindParamCount), value); |
495 | } else { | |
496 | 0 | whereClause.append(JPA_INITIAL_ALIAS).append('.'); |
497 | 0 | return addAttr(fixPeriods(attribute), value); |
498 | } | |
499 | } | |
500 | ||
501 | /* | |
502 | * Adds a new input parameter and its value to the parameter Map, auto-generating a new key if the provided one already exists. | |
503 | * Returns the finalized key as a "__JPA_PARAM[['...']]__" String with a prepended ":". | |
504 | */ | |
505 | private String addAttr(String string, Object value) { | |
506 | 0 | while (params.containsKey(string)) { |
507 | 0 | string = "bind_param" + (++generatedBindParamCount); |
508 | } | |
509 | 0 | params.put(string, value); |
510 | 0 | return new StringBuilder(45).append(JPA_PARAM_PREFIX_WITH_COLON).append('\'').append(string).append('\'').append(JPA_ALIAS_SUFFIX).toString(); |
511 | } | |
512 | ||
513 | /** | |
514 | * Adds a new BETWEEN condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
515 | * | |
516 | * @param attribute An expression representing the subject of the BETWEEN condition. | |
517 | * @param value1 The lower value of the BETWEEN condition. | |
518 | * @param value2 The upper value of the BETWEEN condition. | |
519 | */ | |
520 | public void between(String attribute, Object value1, Object value2) { | |
521 | 0 | preparePrefixIfNecessary(attribute); |
522 | 0 | String[] fixedAttrs = prepareAttributes(attribute, value1, value2); |
523 | 0 | whereClause.append(attribute).append(" BETWEEN ").append(fixedAttrs[0]).append(" AND ").append(fixedAttrs[1]); |
524 | 0 | } |
525 | ||
526 | /** | |
527 | * Adds a new NOT BETWEEN condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
528 | * | |
529 | * @param attribute An expression representing the subject of the NOT BETWEEN condition. | |
530 | * @param value1 The lower value of the NOT BETWEEN condition. | |
531 | * @param value2 The upper value of the NOT BETWEEN condition. | |
532 | */ | |
533 | public void notBetween(String attribute, Object value1, Object value2) { | |
534 | 0 | preparePrefixIfNecessary(attribute); |
535 | 0 | String[] fixedAttrs = prepareAttributes(attribute, value1, value2); |
536 | 0 | whereClause.append(attribute).append(" NOT BETWEEN ").append(fixedAttrs[0]).append(" AND ").append(fixedAttrs[1]); |
537 | 0 | } |
538 | ||
539 | /** | |
540 | * Adds an equality condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
541 | * | |
542 | * @param attribute An expression representing the left-hand side of this condition. | |
543 | * @param value The value to check for equality with. | |
544 | */ | |
545 | public void eq(String attribute, Object value) { | |
546 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, value); |
547 | 0 | whereClause.append(attribute).append(" = ").append(fixedAttr); |
548 | 0 | } |
549 | ||
550 | /** | |
551 | * Adds a greater-than condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
552 | * | |
553 | * @param attribute An expression representing the left-hand side of this condition. | |
554 | * @param value The value on the right-hand side of the greater-than condition. | |
555 | */ | |
556 | public void gt(String attribute, Object value) { | |
557 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, value); |
558 | 0 | whereClause.append(attribute).append(" > ").append(fixedAttr); |
559 | 0 | } |
560 | ||
561 | /** | |
562 | * Adds a greater-than-or-equal condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
563 | * | |
564 | * @param attribute An expression representing the left-hand side of this condition. | |
565 | * @param value The value on the right-hand side of the greater-than-or-equal condition. | |
566 | */ | |
567 | public void gte(String attribute, Object value) { | |
568 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, value); |
569 | 0 | whereClause.append(attribute).append(" >= ").append(fixedAttr); |
570 | 0 | } |
571 | ||
572 | /** | |
573 | * Adds a new LIKE condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
574 | * If the provided value is not a String, its toString() representation will be used as the pattern. | |
575 | * | |
576 | * @param attribute An expression representing the left-hand side of this condition. | |
577 | * @param value The pattern to compare with for "likeness". | |
578 | */ | |
579 | public void like(String attribute, Object value) { | |
580 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, fixSearchPattern(value.toString())); |
581 | 0 | whereClause.append(attribute).append(" LIKE ").append(fixedAttr); |
582 | 0 | } |
583 | ||
584 | /** | |
585 | * Adds a new NOT LIKE condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
586 | * If the provided value is not a String, its toString() representation will be used as the pattern. | |
587 | * | |
588 | * @param attribute An expression representing the left-hand side of this condition. | |
589 | * @param value The pattern to compare with for "non-likeness". | |
590 | */ | |
591 | public void notLike(String attribute, Object value) { | |
592 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, fixSearchPattern(value.toString())); |
593 | 0 | whereClause.append(attribute).append(" NOT LIKE ").append(fixedAttr); |
594 | 0 | } |
595 | ||
596 | /** | |
597 | * Adds a new LIKE condition (containing an ESCAPE clause) to the WHERE clause, ANDing it with any | |
598 | * existing conditions if necessary. | |
599 | * If the provided value is not a String, its toString() representation will be used as the pattern. | |
600 | * | |
601 | * @param attribute An expression representing the left-hand side of this condition. | |
602 | * @param value The pattern to compare with for "likeness". | |
603 | * @param escapeChar The designated wildcard escape character for the given value. | |
604 | */ | |
605 | public void likeEscape(String attribute, Object value, char escapeChar) { | |
606 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, fixSearchPattern(value.toString())); |
607 | 0 | whereClause.append(attribute).append(" LIKE ").append(fixedAttr).append(" ESCAPE ").append( |
608 | prepareAttribute(JPA_ALIAS_PREFIX, Character.valueOf(escapeChar))); | |
609 | 0 | } |
610 | ||
611 | /** | |
612 | * Adds a new NOT LIKE condition (containing an ESCAPE clause) to the WHERE clause, ANDing it with any | |
613 | * existing conditions if necessary. | |
614 | * If the provided value is not a String, its toString() representation will be used as the pattern. | |
615 | * | |
616 | * @param attribute An expression representing the left-hand side of this condition. | |
617 | * @param value The pattern to compare with for "non-likeness". | |
618 | * @param escapeChar The designated wildcard escape character for the given value. | |
619 | */ | |
620 | public void notLikeEscape(String attribute, Object value, char escapeChar) { | |
621 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, fixSearchPattern(value.toString())); |
622 | 0 | whereClause.append(attribute).append(" NOT LIKE ").append(fixedAttr).append(" ESCAPE ").append( |
623 | prepareAttribute(JPA_ALIAS_PREFIX, Character.valueOf(escapeChar))); | |
624 | 0 | } |
625 | ||
626 | /** | |
627 | * Adds a less-than condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
628 | * | |
629 | * @param attribute An expression representing the left-hand side of this condition. | |
630 | * @param value The value on the right-hand side of the less-than condition. | |
631 | */ | |
632 | public void lt(String attribute, Object value) { | |
633 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, value); |
634 | 0 | whereClause.append(attribute).append(" < ").append(fixedAttr); |
635 | 0 | } |
636 | ||
637 | /** | |
638 | * Adds a less-than-or-equal condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
639 | * | |
640 | * @param attribute An expression representing the left-hand side of this condition. | |
641 | * @param value The value on the right-hand side of the less-than-or-equal condition. | |
642 | */ | |
643 | public void lte(String attribute, Object value) { | |
644 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, value); |
645 | 0 | whereClause.append(attribute).append(" <= ").append(fixedAttr); |
646 | 0 | } |
647 | ||
648 | /** | |
649 | * Adds a non-equality condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
650 | * | |
651 | * @param attribute An expression representing the left-hand side of this condition. | |
652 | * @param value The value on the right-hand side of the non-equality condition. | |
653 | */ | |
654 | public void ne(String attribute, Object value) { | |
655 | 0 | String fixedAttr = preparePrefixAndAttributeIfNecessary(attribute, value); |
656 | 0 | whereClause.append(attribute).append(" <> ").append(fixedAttr); |
657 | 0 | } |
658 | ||
659 | /** | |
660 | * Adds an IS NULL condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
661 | * | |
662 | * @param attribute An expression representing what needs the null check. | |
663 | */ | |
664 | public void isNull(String attribute) { | |
665 | 0 | preparePrefixIfNecessary(attribute); |
666 | 0 | whereClause.append(attribute).append(" IS NULL"); |
667 | 0 | } |
668 | ||
669 | /** | |
670 | * Adds an IS NOT NULL condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
671 | * | |
672 | * @param attribute An expression representing what needs the not-null check. | |
673 | */ | |
674 | public void notNull(String attribute) { | |
675 | 0 | preparePrefixIfNecessary(attribute); |
676 | 0 | whereClause.append(attribute).append(" IS NOT NULL"); |
677 | 0 | } |
678 | ||
679 | /** | |
680 | * Adds a MEMBER OF condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
681 | * Uses an input parameter for the single-value part of the condition. | |
682 | * | |
683 | * @param value An Object that may or may not be in the specified collection. | |
684 | * @param collection An expression representing a collection. | |
685 | */ | |
686 | public void memberOf(Object value, String collection) { | |
687 | 0 | String fixedAttr = prepareAttribute(collection, value); |
688 | 0 | preparePrefixIfNecessary(JPA_ALIAS_PREFIX); |
689 | 0 | whereClause.append(fixedAttr).append(" MEMBER OF ").append( |
690 | collection.contains(JPA_ALIAS_PREFIX) ? "" : JPA_INITIAL_ALIAS + ".").append(collection); | |
691 | 0 | } |
692 | ||
693 | /** | |
694 | * Adds a MEMBER OF condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
695 | * Uses an expression for the single-value part of the condition. | |
696 | * | |
697 | * @param attribute An expression pointing to an attribute that may or may not be in the given collection. | |
698 | * @param collection An expression representing a collection. | |
699 | */ | |
700 | public void memberOf(String attribute, String collection) { | |
701 | 0 | preparePrefixIfNecessary(attribute); |
702 | 0 | whereClause.append(attribute).append(" MEMBER OF ").append( |
703 | collection.contains(JPA_ALIAS_PREFIX) ? "" : JPA_INITIAL_ALIAS + ".").append(collection); | |
704 | 0 | } |
705 | ||
706 | /** | |
707 | * Adds a NOT MEMBER OF condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
708 | * Uses an input parameter for the single-value part of the condition. | |
709 | * | |
710 | * @param value An Object that may or may not be in the specified collection. | |
711 | * @param collection An expression representing a collection. | |
712 | */ | |
713 | public void notMemberOf(Object value, String collection) { | |
714 | 0 | String fixedAttr = prepareAttribute(collection, value); |
715 | 0 | preparePrefixIfNecessary(JPA_ALIAS_PREFIX); |
716 | 0 | whereClause.append(fixedAttr).append(" NOT MEMBER OF ").append( |
717 | collection.contains(JPA_ALIAS_PREFIX) ? "" : JPA_INITIAL_ALIAS + ".").append(collection); | |
718 | 0 | } |
719 | ||
720 | /** | |
721 | * Adds a NOT MEMBER OF condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
722 | * Uses an expression for the single-value part of the condition. | |
723 | * | |
724 | * @param attribute An expression pointing to an attribute that may or may not be in the given collection. | |
725 | * @param collection An expression representing a collection. | |
726 | */ | |
727 | public void notMemberOf(String attribute, String collection) { | |
728 | 0 | preparePrefixIfNecessary(attribute); |
729 | 0 | whereClause.append(attribute).append(" NOT MEMBER OF ").append( |
730 | collection.contains(JPA_ALIAS_PREFIX) ? "" : JPA_INITIAL_ALIAS + ".").append(collection); | |
731 | 0 | } |
732 | ||
733 | /** | |
734 | * Adds an IS EMPTY condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
735 | * | |
736 | * @param collection An expression representing a collection. | |
737 | */ | |
738 | public void isEmpty(String collection) { | |
739 | 0 | preparePrefixIfNecessary(collection); |
740 | 0 | whereClause.append(collection).append(" IS EMPTY"); |
741 | 0 | } |
742 | ||
743 | /** | |
744 | * Adds an IS NOT EMPTY condition to the WHERE clause, ANDing it with any existing conditions if necessary. | |
745 | * | |
746 | * @param collection An expression representing a collection. | |
747 | */ | |
748 | public void notEmpty(String collection) { | |
749 | 0 | preparePrefixIfNecessary(collection); |
750 | 0 | whereClause.append(collection).append(" IS NOT EMPTY"); |
751 | 0 | } |
752 | ||
753 | /** | |
754 | * Converts the given Criteria into a sub-SELECT query, surrounds it in parentheses, and adds a new | |
755 | * "IN (...)" condition containing the resulting expression into the WHERE clause, while ANDing | |
756 | * the new condition with any existing conditions if necessary. | |
757 | * | |
758 | * @param attribute An expression representing the left-hand side of the "IN (...)" condition. | |
759 | * @param in The Criteria representing the sub-query of the "IN (...)" condition. | |
760 | */ | |
761 | public void in(String attribute, Criteria in) { | |
762 | 0 | if (in == null) { throw new IllegalArgumentException("'IN' Criteria cannot be null"); } |
763 | 0 | preparePrefixIfNecessary(attribute); |
764 | 0 | whereClause.append(attribute).append(" IN "); |
765 | 0 | subQuery(in); |
766 | 0 | } |
767 | ||
768 | /** | |
769 | * Converts the given Criteria into a sub-SELECT query, surrounds it in parentheses, and adds a new | |
770 | * "NOT IN (...)" condition containing the resulting expression into the WHERE clause, while ANDing | |
771 | * the new condition with any existing conditions if necessary. | |
772 | * | |
773 | * @param attribute An expression representing the left-hand side of the "NOT IN (...)" condition. | |
774 | * @param in The Criteria representing the sub-query of the "NOT IN (...)" condition. | |
775 | */ | |
776 | public void notIn(String attribute, Criteria notIn) { | |
777 | 0 | if (notIn == null) { throw new IllegalArgumentException("'NOT IN' Criteria cannot be null"); } |
778 | 0 | preparePrefixIfNecessary(attribute); |
779 | 0 | whereClause.append(attribute).append(" NOT IN "); |
780 | 0 | subQuery(notIn); |
781 | 0 | } |
782 | ||
783 | /** | |
784 | * Takes the individual elements of the provided collection, converts them to literals or assigns them to input | |
785 | * parameters as needed, and adds a new "IN (...)" condition to the WHERE clause containing those elements, | |
786 | * ANDing it with any existing conditions if necessary. String, number, boolean, and Class collection elements | |
787 | * will be converted into literals, and all other non-null collection elements will be assigned to input parameters. | |
788 | * | |
789 | * @param attribute An expression representing the left-hand side of the "IN (...)" condition. | |
790 | * @param values The collection of values to check against. | |
791 | */ | |
792 | public void in(String attribute, Collection<?> values) { | |
793 | 0 | preparePrefixIfNecessary(attribute); |
794 | 0 | whereClause.append(attribute).append(" IN ("); |
795 | 0 | appendBodyOfIn(values); |
796 | 0 | whereClause.append(')'); |
797 | 0 | } |
798 | ||
799 | /** | |
800 | * Takes the individual elements of the provided collection, converts them to literals or assigns them to input | |
801 | * parameters as needed, and adds a new "IN (...)" condition to the WHERE clause containing those elements, | |
802 | * ANDing it with any existing conditions if necessary. String, number, boolean, and Class collection elements | |
803 | * will be converted into literals, and all other non-null collection elements will be assigned to input parameters. | |
804 | * | |
805 | * @param attribute An expression representing the left-hand side of the "NOT IN (...)" condition. | |
806 | * @param values The collection of values to check against. | |
807 | */ | |
808 | public void notIn(String attribute, Collection<?> values) { | |
809 | 0 | preparePrefixIfNecessary(attribute); |
810 | 0 | whereClause.append(attribute).append(" NOT IN ("); |
811 | 0 | appendBodyOfIn(values); |
812 | 0 | whereClause.append(')'); |
813 | 0 | } |
814 | ||
815 | /** | |
816 | * Takes the provided values, converts them to literals or assigns them to input parameters as needed, and adds | |
817 | * a new "IN (...)" condition to the WHERE clause containing those elements, ANDing it with any existing | |
818 | * conditions if necessary. String, number, boolean, and Class values will be converted into literals, and all | |
819 | * other non-null values will be assigned to input parameters. | |
820 | * | |
821 | * @param attribute An expression representing the left-hand side of the "IN (...)" condition. | |
822 | * @param values The values to check against. | |
823 | */ | |
824 | public void in(String attribute, Object... values) { | |
825 | 0 | preparePrefixIfNecessary(attribute); |
826 | 0 | whereClause.append(attribute).append(" IN ("); |
827 | 0 | appendBodyOfIn(Arrays.asList(values)); |
828 | 0 | whereClause.append(')'); |
829 | 0 | } |
830 | ||
831 | /** | |
832 | * Takes the provided values, converts them to literals or assigns them to input parameters as needed, and adds | |
833 | * a new "NOT IN (...)" condition to the WHERE clause containing those elements, ANDing it with any existing | |
834 | * conditions if necessary. String, number, boolean, and Class values will be converted into literals, and all | |
835 | * other non-null values will be assigned to input parameters. | |
836 | * | |
837 | * @param attribute An expression representing the left-hand side of the "NOT IN (...)" condition. | |
838 | * @param values The values to check against. | |
839 | */ | |
840 | public void notIn(String attribute, Object... values) { | |
841 | 0 | preparePrefixIfNecessary(attribute); |
842 | 0 | whereClause.append(attribute).append(" NOT IN ("); |
843 | 0 | appendBodyOfIn(Arrays.asList(values)); |
844 | 0 | whereClause.append(')'); |
845 | 0 | } |
846 | ||
847 | /* | |
848 | * Creates the body of an "IN (...)" condition using the provided collection of values. | |
849 | * String, number, and boolean values are converted into literals, and all other | |
850 | * non-null values are assigned to input parameters. | |
851 | */ | |
852 | private void appendBodyOfIn(Collection<?> values) { | |
853 | 0 | Iterator<?> valuesIter = values.iterator(); |
854 | 0 | if (valuesIter.hasNext()) { |
855 | 0 | fixValue(whereClause, valuesIter.next()); |
856 | 0 | while (valuesIter.hasNext()) { |
857 | 0 | whereClause.append(", "); |
858 | 0 | fixValue(whereClause, valuesIter.next()); |
859 | } | |
860 | } | |
861 | 0 | } |
862 | ||
863 | /** | |
864 | * Takes the conditions from the given Criteria's WHERE clause, surrounds them in parentheses, and ANDs | |
865 | * the resulting expression with this instance's WHERE clause (or makes the expression become the new | |
866 | * WHERE clause of this instance if its current WHERE clause is empty). Any input parameters from the | |
867 | * given Criteria will be copied over to this Criteria, and will be renamed as needed if conflicts arise. | |
868 | * | |
869 | * @param and The Criteria instance whose conditions should be ANDed with those in this Criteria instance. | |
870 | */ | |
871 | public void and(Criteria and) { | |
872 | 0 | if (and == null) { throw new IllegalArgumentException("'AND' Criteria cannot be null"); } |
873 | 0 | if (and.whereClause.length() > 0) { |
874 | 0 | int oldLen = whereClause.length(); |
875 | 0 | preparePrefixIfNecessary(JPA_ALIAS_PREFIX); |
876 | 0 | whereClause.append('(').append(and.whereClause).append(')'); |
877 | 0 | copyParams(and, oldLen, true); |
878 | } | |
879 | 0 | } |
880 | ||
881 | /** | |
882 | * Takes the conditions from the given Criteria's WHERE clause, surrounds them in parentheses, and adds | |
883 | * a new "NOT (...)" condition containing the resulting expression to this instance's WHERE clause (or | |
884 | * adds a new "AND NOT(...)" condition if the WHERE clause on the existing Criteria is not empty). Any | |
885 | * input parameters from the given Criteria will be copied over to this Criteria, and will be renamed | |
886 | * as needed if conflicts arise. | |
887 | * | |
888 | * @param not The Criteria instance whose group of conditions should be negated. | |
889 | */ | |
890 | public void not(Criteria not) { | |
891 | 0 | if (not == null) { throw new IllegalArgumentException("'NOT' Criteria cannot be null"); } |
892 | 0 | if (not.whereClause.length() > 0) { |
893 | 0 | int oldLen = whereClause.length(); |
894 | 0 | preparePrefixIfNecessary(JPA_ALIAS_PREFIX); |
895 | 0 | whereClause.append("NOT (").append(not.whereClause).append(')'); |
896 | 0 | copyParams(not, oldLen, true); |
897 | } | |
898 | 0 | } |
899 | ||
900 | /** | |
901 | * Takes the conditions from the given Criteria's WHERE clause, surrounds them in parentheses, and ORs | |
902 | * the resulting expression with this instance's WHERE clause (or makes the expression become the new | |
903 | * WHERE clause of this instance if its current WHERE clause is empty). Any input parameters from the | |
904 | * given Criteria will be copied over to this Criteria, and will be renamed as needed if conflicts arise. | |
905 | * | |
906 | * @param or The Criteria instance whose conditions should be ORed with those in this Criteria instance. | |
907 | */ | |
908 | public void or(Criteria or) { | |
909 | 0 | if (or == null) { throw new IllegalArgumentException("'OR' Criteria cannot be null"); } |
910 | 0 | if (or.whereClause.length() > 0) { |
911 | 0 | int oldLen = whereClause.length(); |
912 | 0 | whereClause.append((oldLen > 0) ? " OR (" : "(").append(or.whereClause).append(')'); |
913 | 0 | copyParams(or, oldLen, true); |
914 | } | |
915 | 0 | } |
916 | ||
917 | /** | |
918 | * Takes the conditions from the given Criteria's WHERE clause, surrounds them in parentheses, and adds | |
919 | * a new "OR NOT (...)" condition containing the resulting expression to this instance's WHERE clause (or | |
920 | * just adds a new "NOT (...)" condition if its current WHERE clause is empty). Any input parameters from the | |
921 | * given Criteria will be copied over to this Criteria, and will be renamed as needed if conflicts arise. | |
922 | * | |
923 | * @param orNot The Criteria instance whose group of conditions should be negated and then ORed with those in this Criteria instance. | |
924 | */ | |
925 | public void orNot(Criteria orNot) { | |
926 | 0 | if (orNot == null) { throw new IllegalArgumentException("'OR NOT' Criteria cannot be null"); } |
927 | 0 | if (orNot.whereClause.length() > 0) { |
928 | 0 | int oldLen = whereClause.length(); |
929 | 0 | whereClause.append((oldLen > 0) ? " OR NOT (" : "NOT (").append(orNot.whereClause).append(')'); |
930 | 0 | copyParams(orNot, oldLen, true); |
931 | } | |
932 | 0 | } |
933 | ||
934 | /** | |
935 | * Converts the given Criteria into a sub-SELECT query, surrounds it in parentheses, and adds a new | |
936 | * EXISTS condition containing the resulting expression into the WHERE clause, while ANDing the new | |
937 | * condition with any existing conditions if necessary. | |
938 | * | |
939 | * @param exists The Criteria instance representing the sub-query of the EXISTS condition. | |
940 | */ | |
941 | public void exists(Criteria exists) { | |
942 | 0 | if (exists == null) { throw new IllegalArgumentException("'EXISTS' Criteria cannot be null"); } |
943 | 0 | preparePrefixIfNecessary(JPA_ALIAS_PREFIX); |
944 | 0 | whereClause.append("EXISTS "); |
945 | 0 | subQuery(exists); |
946 | 0 | } |
947 | ||
948 | /** | |
949 | * Converts the given Criteria into a sub-SELECT query, surrounds it in parentheses, and adds a new | |
950 | * NOT EXISTS condition containing the resulting expression into the WHERE clause, while ANDing the | |
951 | * new condition with any existing conditions if necessary. | |
952 | * | |
953 | * @param exists The Criteria instance representing the sub-query of the NOT EXISTS condition. | |
954 | */ | |
955 | public void notExists(Criteria notExists) { | |
956 | 0 | if (notExists == null) { throw new IllegalArgumentException("'NOT EXISTS' Criteria cannot be null"); } |
957 | 0 | preparePrefixIfNecessary(JPA_ALIAS_PREFIX); |
958 | 0 | whereClause.append("NOT EXISTS "); |
959 | 0 | subQuery(notExists); |
960 | 0 | } |
961 | ||
962 | /** | |
963 | * Inserts raw JPQL into the WHERE clause. An extra whitespace character will automatically be prepended | |
964 | * (if the WHERE clause is non-empty) and appended to the JPQL string. | |
965 | * | |
966 | * @param jpql The raw JPQL to insert. | |
967 | */ | |
968 | public void rawJpql(String jpql) { | |
969 | 0 | whereClause.append((whereClause.length() > 0) ? " " : "").append(jpql).append(' '); |
970 | 0 | } |
971 | ||
972 | /** | |
973 | * Adds a new expression to the GROUP BY clause. If no properly-referenced alias appears in | |
974 | * the String expression, it will be assumed that the expression points to something | |
975 | * that is accessible on or through the initial entity. See the description of this class | |
976 | * for more information on how String expressions are interpreted. | |
977 | * | |
978 | * @param attribute The expression representing what the query results should be grouped by. | |
979 | */ | |
980 | public void groupBy(String attribute) { | |
981 | 0 | groupByClause.append((groupByClause.length() > 0) ? ", " : " GROUP BY "); |
982 | 0 | if (!attribute.contains(JPA_ALIAS_PREFIX)) { |
983 | 0 | groupByClause.append(JPA_INITIAL_ALIAS).append('.'); |
984 | } | |
985 | 0 | groupByClause.append(attribute); |
986 | 0 | } |
987 | ||
988 | /** | |
989 | * Takes the conditions from the given Criteria's WHERE clause and then ANDs them with this | |
990 | * instance's HAVING clause (or makes them become the new HAVING clause of this instance if | |
991 | * its current HAVING clause is empty). Any input parameters from the given Criteria will be | |
992 | * copied over to this Criteria, and will be renamed as needed if conflicts arise. | |
993 | * | |
994 | * @param having The Criteria instance whose WHERE clause should represent this instance's HAVING clause (or a piece of it). | |
995 | */ | |
996 | public void having(Criteria having) { | |
997 | 0 | if (having == null) { throw new IllegalArgumentException("'HAVING' Criteria cannot be null"); } |
998 | 0 | if (having.whereClause.length() > 0) { |
999 | 0 | int oldLen = havingClause.length(); |
1000 | 0 | havingClause.append((oldLen > 0) ? " AND " : " HAVING ").append(having.whereClause); |
1001 | 0 | copyParams(having, oldLen, false); |
1002 | } | |
1003 | 0 | } |
1004 | ||
1005 | /** | |
1006 | * Adds a new expression to the ORDER BY clause. If no properly-referenced alias appears in | |
1007 | * the String expression, it will be assumed that the expression points to something | |
1008 | * that is accessible on or through the initial entity. See the description of this class | |
1009 | * for more information on how String expressions are interpreted. | |
1010 | * | |
1011 | * @param attribute The expression representing what the query results should be ordered by. | |
1012 | * @param sortAscending If true, the order will be ascending; otherwise, it will be descending. | |
1013 | */ | |
1014 | public void orderBy(String attribute, boolean sortAscending) { | |
1015 | 0 | orderByClause.append((orderByClause.length() > 0) ? ", " : " ORDER BY "); |
1016 | 0 | if (!attribute.contains(JPA_ALIAS_PREFIX)) { |
1017 | 0 | orderByClause.append(JPA_INITIAL_ALIAS).append('.'); |
1018 | } | |
1019 | 0 | orderByClause.append(attribute).append(sortAscending ? " ASC" : " DESC"); |
1020 | 0 | } |
1021 | ||
1022 | /* | |
1023 | * Copies the input parameters from the given child Criteria to the current Criteria. Any parameter placeholders from the sub-query will | |
1024 | * be renamed accordingly if they conflict with those defined by the parent Criteria. | |
1025 | * | |
1026 | * It is assumed that the sub-query has already been appended to the clause given by modifyWhereClause | |
1027 | * (WHERE if true, HAVING if false), starting at the index given by startIndex. | |
1028 | */ | |
1029 | private void copyParams(Criteria subCriteria, int startIndex, boolean modifyWhereClause) { | |
1030 | 0 | final StringBuilder tempClause = modifyWhereClause ? whereClause : havingClause; |
1031 | 0 | Map<String,String> modifiedNames = new HashMap<String,String>(); |
1032 | 0 | int paramClose = 0; |
1033 | 0 | int paramIndex = tempClause.indexOf(JPA_PARAM_PREFIX_WITH_COLON, startIndex); |
1034 | 0 | String newPlaceholder = null; |
1035 | ||
1036 | 0 | for (Map.Entry<String,Object> param : subCriteria.params.entrySet()) { |
1037 | 0 | if (params.containsKey(param.getKey())) { |
1038 | 0 | modifiedNames.put(param.getKey(), addAttr(param.getKey(), param.getValue())); |
1039 | } else { | |
1040 | 0 | params.put(param.getKey(), param.getValue()); |
1041 | } | |
1042 | } | |
1043 | ||
1044 | 0 | while (paramIndex >= 0) { |
1045 | 0 | paramClose = tempClause.indexOf(JPA_ALIAS_SUFFIX, paramIndex); |
1046 | 0 | newPlaceholder = modifiedNames.get(tempClause.substring(paramIndex + PARAM_PREFIX_LEN + 2, paramClose - 1)); |
1047 | 0 | if (newPlaceholder != null) { |
1048 | 0 | tempClause.replace(paramIndex, paramClose + ALIAS_SUFFIX_LEN, newPlaceholder); |
1049 | } | |
1050 | 0 | paramIndex = tempClause.indexOf(JPA_PARAM_PREFIX_WITH_COLON, paramIndex + 1); |
1051 | } | |
1052 | 0 | } |
1053 | ||
1054 | /* | |
1055 | * Converts the given Criteria into a sub-query and appends it to the WHERE clause. Any named aliases of the sub-query will be renamed | |
1056 | * accordingly if they conflict with those defined by the parent Criteria, and all indexed aliases will be updated accordingly. In | |
1057 | * addition, the input parameters will be copied over and renamed as needed. | |
1058 | */ | |
1059 | private void subQuery(Criteria subCriteria) { | |
1060 | 0 | Map<String,String> modifiedAliases = new HashMap<String,String>(); |
1061 | 0 | int count = subCriteria.entityAliases.size(); |
1062 | 0 | int paramIndex = 0; |
1063 | 0 | int paramClose = 0; |
1064 | 0 | String newPlaceholder = null; |
1065 | ||
1066 | 0 | for (int i = entityAliases.size() - 1; i >= 0; i--) { |
1067 | 0 | modifiedAliases.put(JPA_ALIAS_PREFIX + (~i) + JPA_ALIAS_SUFFIX, indexedAliasPlaceholders.get(i)); |
1068 | } | |
1069 | 0 | for (int i = 0; i < count; i++) { |
1070 | 0 | String oldAlias = subCriteria.entityAliases.get(i); |
1071 | 0 | if (!addAlias(oldAlias).equals(oldAlias)) { |
1072 | 0 | modifiedAliases.put(subCriteria.namedAliasPlaceholders.get(i), namedAliasPlaceholders.get(namedAliasPlaceholders.size() - 1)); |
1073 | } | |
1074 | 0 | modifiedAliases.put(subCriteria.indexedAliasPlaceholders.get(i), indexedAliasPlaceholders.get(indexedAliasPlaceholders.size() - 1)); |
1075 | } | |
1076 | ||
1077 | 0 | count = whereClause.length(); |
1078 | 0 | whereClause.append(subCriteria.distinct ? "(SELECT DISTINCT " : "(SELECT ").append(subCriteria.selectClause).append(subCriteria.fromClause).append( |
1079 | (subCriteria.whereClause.length() > 0) ? " WHERE " : "").append(subCriteria.whereClause).append( | |
1080 | subCriteria.groupByClause).append(subCriteria.havingClause).append(')'); | |
1081 | ||
1082 | 0 | paramIndex = whereClause.indexOf(JPA_ALIAS_PREFIX, count); |
1083 | 0 | while (paramIndex >= 0) { |
1084 | 0 | paramClose = whereClause.indexOf(JPA_ALIAS_SUFFIX, paramIndex) + ALIAS_SUFFIX_LEN; |
1085 | 0 | newPlaceholder = modifiedAliases.get(whereClause.substring(paramIndex, paramClose)); |
1086 | 0 | if (newPlaceholder != null) { |
1087 | 0 | whereClause.replace(paramIndex, paramClose, newPlaceholder); |
1088 | } | |
1089 | 0 | paramIndex = whereClause.indexOf(JPA_ALIAS_PREFIX, paramIndex + 1); |
1090 | } | |
1091 | 0 | copyParams(subCriteria, count, true); |
1092 | 0 | } |
1093 | ||
1094 | /** | |
1095 | * Converts the current Criteria instance into a JPQL query. | |
1096 | * | |
1097 | * @param type An indicator of what type of query should be generated (SELECT, UPDATE, or DELETE). | |
1098 | * @return A String representing the JPQL query generated by this Criteria instance. | |
1099 | */ | |
1100 | public String toQuery(QueryByCriteria.QueryByCriteriaType type) { | |
1101 | 0 | return toQuery(type, new String[0]); |
1102 | } | |
1103 | ||
1104 | /** | |
1105 | * Converts the current Criteria instance into a JPQL query. If a SELECT query is desired, then | |
1106 | * alternative String expressions to insert into the SELECT clause can be specified, though doing | |
1107 | * so is not required and must be done with care (see below). | |
1108 | * | |
1109 | * <p>WARNING! If queryAttr is non-null and has a length greater than zero, and if a SELECT query is | |
1110 | * desired, then this method will completely overwrite the SELECT clause so that it only contains | |
1111 | * the expressions given by the queryAttr array. | |
1112 | * | |
1113 | * @param type An indicator of what type of query should be generated (SELECT, UPDATE, or DELETE). | |
1114 | * @param queryAttr (Optional) An array of String expressions to use as the values to be returned by the SELECT clause (if creating a SELECT query). | |
1115 | * @return A String representing the JPQL query generated by this Criteria instance. | |
1116 | */ | |
1117 | public String toQuery(QueryByCriteria.QueryByCriteriaType type, String[] queryAttr) { | |
1118 | StringBuilder newQuery; | |
1119 | 0 | int querySize = selectClause.length() + fromClause.length() + whereClause.length() + groupByClause.length() + havingClause.length() + orderByClause.length() + 7; |
1120 | // Construct the Criteria appropriately. | |
1121 | 0 | switch (type) { |
1122 | case SELECT : | |
1123 | 0 | if (queryAttr != null && queryAttr.length > 0) { |
1124 | 0 | if (selectClause.length() > 0) { selectClause.delete(0, selectClause.length()); } |
1125 | 0 | for (int i = 0; i < queryAttr.length; i++) { |
1126 | 0 | select(queryAttr[i]); |
1127 | } | |
1128 | } | |
1129 | 0 | newQuery = new StringBuilder(querySize + (distinct ? 16 : 7)).append(distinct ? "SELECT DISTINCT " : "SELECT ").append( |
1130 | selectClause).append(fromClause).append(whereClause.length() > 0 ? " WHERE " : "").append(whereClause).append( | |
1131 | groupByClause).append(havingClause).append(orderByClause); | |
1132 | 0 | break; |
1133 | case UPDATE : | |
1134 | 0 | newQuery = new StringBuilder(querySize + setClause.length() + 7).append("UPDATE ").append(initialEntityName).append(" AS ").append( |
1135 | getAlias()).append(setClause).append(whereClause.length() > 0 ? " WHERE " : "").append(whereClause); | |
1136 | 0 | break; |
1137 | case DELETE : | |
1138 | 0 | newQuery = new StringBuilder(querySize + 6).append("DELETE").append(fromClause).append( |
1139 | whereClause.length() > 0 ? " WHERE " : "").append(whereClause); | |
1140 | 0 | break; |
1141 | default : | |
1142 | 0 | return null; |
1143 | } | |
1144 | 0 | return fix(newQuery); |
1145 | } | |
1146 | ||
1147 | /** | |
1148 | * Converts the current Criteria instance into a JPQL SELECT query, but replaces the existing SELECT clause with "SELECT COUNT(*)" instead. | |
1149 | * | |
1150 | * @return A JPQL query String given by this Criteria, but with "SELECT COUNT(*)" as the SELECT clause instead. | |
1151 | */ | |
1152 | public String toCountQuery() { | |
1153 | 0 | StringBuilder newQuery = new StringBuilder( |
1154 | fromClause.length() + whereClause.length() + groupByClause.length() + havingClause.length() + orderByClause.length() + 15 + 7).append( | |
1155 | "SELECT COUNT(*)").append(fromClause).append(whereClause.length() > 0 ? " WHERE " : "").append(whereClause).append( | |
1156 | groupByClause).append(havingClause).append(orderByClause); | |
1157 | 0 | return fix(newQuery); |
1158 | } | |
1159 | ||
1160 | /* | |
1161 | * Converts all the named and indexed alias/parameter placeholders into the actual aliases/parameters. | |
1162 | * You can view the resulting JPQL String in the logs by configuring a log4j DEBUG output level for | |
1163 | * this class. | |
1164 | */ | |
1165 | private String fix(StringBuilder newQuery) { | |
1166 | 0 | Map<String,String> modifiedAliases = new HashMap<String,String>(); |
1167 | 0 | for (int i = entityAliases.size() - 1; i >= 0; i--) { |
1168 | 0 | modifiedAliases.put(indexedAliasPlaceholders.get(i), entityAliases.get(i)); |
1169 | 0 | modifiedAliases.put(namedAliasPlaceholders.get(i), entityAliases.get(i)); |
1170 | } | |
1171 | ||
1172 | // resolve the aliases. | |
1173 | 0 | String newParam = null; |
1174 | 0 | int paramClose = 0; |
1175 | 0 | int paramIndex = newQuery.indexOf(JPA_ALIAS_PREFIX); |
1176 | 0 | while (paramIndex >= 0) { |
1177 | 0 | paramClose = newQuery.indexOf(JPA_ALIAS_SUFFIX, paramIndex) + ALIAS_SUFFIX_LEN; |
1178 | 0 | newParam = modifiedAliases.get(newQuery.substring(paramIndex, paramClose)); |
1179 | 0 | if (newParam != null) { |
1180 | 0 | newQuery.replace(paramIndex, paramClose, newParam); |
1181 | } else { | |
1182 | 0 | LOG.error("Detected an unresolvable JPA alias when constructing query: " + newQuery.substring(paramIndex, paramClose)); |
1183 | 0 | throw new IllegalStateException("Detected an unresolvable alias: " + newQuery.substring(paramIndex, paramClose)); |
1184 | } | |
1185 | 0 | paramIndex = newQuery.indexOf(JPA_ALIAS_PREFIX, paramIndex + 1); |
1186 | } | |
1187 | ||
1188 | // Resolve the parameters. | |
1189 | 0 | paramIndex = newQuery.indexOf(JPA_PARAM_PREFIX); |
1190 | 0 | while (paramIndex >= 0) { |
1191 | 0 | paramClose = newQuery.indexOf(JPA_ALIAS_SUFFIX, paramIndex); |
1192 | 0 | newParam = newQuery.substring(paramIndex + PARAM_PREFIX_LEN + 1, paramClose - 1); |
1193 | 0 | if (params.containsKey(newParam)) { |
1194 | 0 | newQuery.replace(paramIndex, paramClose + ALIAS_SUFFIX_LEN, newParam); |
1195 | } else { | |
1196 | 0 | LOG.error("Detected an unresolvable input parameter when constructing query: " + newQuery.substring(paramIndex, paramClose + ALIAS_SUFFIX_LEN)); |
1197 | 0 | throw new IllegalStateException("Detected an unresolvable input parameter: " + newQuery.substring(paramIndex, paramClose + ALIAS_SUFFIX_LEN)); |
1198 | } | |
1199 | 0 | paramIndex = newQuery.indexOf(JPA_PARAM_PREFIX, paramIndex + 1); |
1200 | } | |
1201 | ||
1202 | 0 | String finishedQuery = newQuery.toString(); |
1203 | 0 | if (LOG.isDebugEnabled()) { |
1204 | 0 | LOG.debug("********** SEARCH JPQL QUERY **********"); |
1205 | 0 | LOG.debug(finishedQuery); |
1206 | 0 | LOG.debug("***************************************"); |
1207 | } | |
1208 | 0 | return finishedQuery; |
1209 | } | |
1210 | ||
1211 | // Keep this package access so the QueryByCriteria can call it from this package. | |
1212 | /** | |
1213 | * Populates the given Query instance with the parameters stored in this Criteria. It is assumed that the given query | |
1214 | * is using this Criteria's toQuery() or toCountQuery() value as its JPQL String. | |
1215 | * | |
1216 | * @param query A JPA Query instance containing this Criteria's toQuery() or toCountQuery() value for its JPQL String. | |
1217 | */ | |
1218 | public void prepareParameters(Query query) { | |
1219 | 0 | for (Map.Entry<String, Object> param : params.entrySet()) { |
1220 | 0 | Object value = param.getValue(); |
1221 | 0 | if (value != null) { |
1222 | 0 | if (value instanceof String) { |
1223 | 0 | if (query.getParameter(param.getKey()).getParameterType().equals(java.lang.Boolean.class)) { |
1224 | 0 | value = Truth.strToBooleanIgnoreCase((String) value, Boolean.FALSE); |
1225 | } else { | |
1226 | //value = ((String)value).replaceAll("\\*", "%"); | |
1227 | } | |
1228 | } | |
1229 | 0 | query.setParameter(param.getKey(), value); |
1230 | } | |
1231 | 0 | } |
1232 | 0 | } |
1233 | ||
1234 | /** | |
1235 | * Retrieves the current limit on the result set size for this Criteria, if any (only relevant if creating a SELECT query). | |
1236 | * | |
1237 | * @return The current limit on the number of results to be returned by this Criteria's query, or null (default) if no such limit has been set. | |
1238 | */ | |
1239 | public Integer getSearchLimit() { | |
1240 | 0 | return this.searchLimit; |
1241 | } | |
1242 | ||
1243 | /** | |
1244 | * Sets the current limit on the result set size for this Criteria (only relevant if creating a SELECT query). | |
1245 | * | |
1246 | * @param searchLimit The new limit on the number of results to be returned by this Criteria's query. | |
1247 | */ | |
1248 | public void setSearchLimit(Integer searchLimit) { | |
1249 | 0 | this.searchLimit = searchLimit; |
1250 | 0 | } |
1251 | ||
1252 | /** | |
1253 | * Sets whether or not the query should include the DISTINCT keyword in the SELECT clause, if creating a SELECT query. | |
1254 | * If this property is not set explicitly, it is assumed that DISTINCT should *not* be included in the SELECT clause. | |
1255 | * | |
1256 | * @param distinct An indicator for whether to do a SELECT DISTINCT or just a SELECT. | |
1257 | */ | |
1258 | public void distinct(boolean distinct){ | |
1259 | 0 | this.distinct = distinct; |
1260 | 0 | } |
1261 | ||
1262 | /** | |
1263 | * Retrieves the initial alias of this Criteria instance. | |
1264 | * | |
1265 | * @return The initial alias. | |
1266 | */ | |
1267 | public String getAlias() { | |
1268 | 0 | return this.entityAliases.get(0); |
1269 | } | |
1270 | ||
1271 | /** | |
1272 | * Retrieves the alias associated with the given index. | |
1273 | * | |
1274 | * @param index The index pointing to a given alias. | |
1275 | * @return The alias at the given index. | |
1276 | */ | |
1277 | public String getAlias(int index) { | |
1278 | 0 | return this.entityAliases.get(index); |
1279 | } | |
1280 | ||
1281 | /** | |
1282 | * Retrieves the index that points to the given alias. | |
1283 | * | |
1284 | * @param alias The indexed alias. | |
1285 | * @return The index of the alias, or -1 if no such index was found. | |
1286 | */ | |
1287 | public int getAliasIndex(String alias) { | |
1288 | 0 | Integer tempIndex = aliasIndexes.get(alias); |
1289 | 0 | return (tempIndex != null) ? tempIndex.intValue() : -1; |
1290 | } | |
1291 | ||
1292 | /** | |
1293 | * Retrieves a copy of all the aliases defined for this Criteria instance. | |
1294 | * The index of a given alias corresponds to the index at which the alias | |
1295 | * is located at in the returned List. | |
1296 | * | |
1297 | * @return A List containing all the defined aliases of this Criteria instance. | |
1298 | */ | |
1299 | public List<String> getAliases() { | |
1300 | 0 | return new ArrayList(this.entityAliases); |
1301 | } | |
1302 | ||
1303 | /** | |
1304 | * @return the name of the class of the initial Entity this Criteria will search for | |
1305 | */ | |
1306 | public String getEntityName() { | |
1307 | 0 | return this.initialEntityName; |
1308 | } | |
1309 | } |