26
26
* @author Kevin Le Saulnier <[email protected] >
27
27
*/
28
28
public final class SpecificationUtils {
29
- /**
30
- * Maximum values per IN clause chunk to avoid StackOverflow exceptions.
31
- * Current value (500) is a safe default but can be changed
32
- */
33
- public static final int MAX_IN_CLAUSE_SIZE = 500 ;
34
29
35
30
public static final String FIELD_SEPARATOR = "." ;
36
31
32
+ /**
33
+ Prefer less than 1000 for Oracle DB / good compromise for Postgres
34
+ */
35
+ public static final int MAX_IN_CLAUSE_SIZE = 10000 ;
36
+
37
37
// Utility class, so no constructor
38
38
private SpecificationUtils () { }
39
39
@@ -51,6 +51,11 @@ public static <X> Specification<X> notEqual(String field, String value) {
51
51
return (root , cq , cb ) -> cb .notEqual (getColumnPath (root , field ), value );
52
52
}
53
53
54
+ public static <X > Specification <X > in (String field , List <String > values ) {
55
+ return (root , cq , cb ) ->
56
+ cb .upper (getColumnPath (root , field ).as (String .class )).in (values );
57
+ }
58
+
54
59
public static <X > Specification <X > contains (String field , String value ) {
55
60
return (root , cq , cb ) -> cb .like (cb .upper (getColumnPath (root , field ).as (String .class )), "%" + EscapeCharacter .DEFAULT .escape (value ).toUpperCase () + "%" , EscapeCharacter .DEFAULT .getEscapeCharacter ());
56
61
}
@@ -135,15 +140,22 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio
135
140
// implicitly an IN resourceFilter type because only IN may have value lists as filter value
136
141
List <String > inValues = valueList .stream ()
137
142
.map (Object ::toString )
143
+ .map (String ::toUpperCase )
138
144
.toList ();
139
145
completedSpecification = completedSpecification .and (
146
+ resourceFilter .type () == ResourceFilterDTO .Type .NOT_EQUAL ?
147
+ not (generateInSpecification (resourceFilter .column (), inValues )) :
140
148
generateInSpecification (resourceFilter .column (), inValues )
141
149
);
142
150
} else if (resourceFilter .value () == null ) {
143
151
// if the value is null, we build an impossible specification (trick to remove later on ?)
144
152
completedSpecification = completedSpecification .and (not (completedSpecification ));
145
153
} else {
146
- completedSpecification = completedSpecification .and (equals (resourceFilter .column (), resourceFilter .value ().toString ()));
154
+ completedSpecification = completedSpecification .and (
155
+ resourceFilter .type () == ResourceFilterDTO .Type .NOT_EQUAL ?
156
+ notEqual (resourceFilter .column (), resourceFilter .value ().toString ()) :
157
+ equals (resourceFilter .column (), resourceFilter .value ().toString ())
158
+ );
147
159
}
148
160
}
149
161
case CONTAINS -> {
@@ -176,32 +188,17 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio
176
188
* @return a specification for the IN clause
177
189
*/
178
190
private static <X > Specification <X > generateInSpecification (String column , List <String > inPossibleValues ) {
179
-
180
- if (inPossibleValues .size () > MAX_IN_CLAUSE_SIZE ) {
181
- // there are too many values for only one call to anyOf() : it might cause a StackOverflow
182
- // => the specification is divided into several specifications which have an OR between them :
183
- List <List <String >> chunksOfInValues = Lists .partition (inPossibleValues , MAX_IN_CLAUSE_SIZE );
184
- Specification <X > containerSpec = null ;
185
- for (List <String > chunk : chunksOfInValues ) {
186
- Specification <X > multiOrEqualSpec = anyOf (
187
- chunk
188
- .stream ()
189
- .map (value -> SpecificationUtils .<X >equals (column , value ))
190
- .toList ()
191
- );
192
- if (containerSpec == null ) {
193
- containerSpec = multiOrEqualSpec ;
194
- } else {
195
- containerSpec = containerSpec .or (multiOrEqualSpec );
196
- }
191
+ List <List <String >> chunksOfInValues = Lists .partition (inPossibleValues , MAX_IN_CLAUSE_SIZE );
192
+ Specification <X > containerSpec = null ;
193
+ for (List <String > chunk : chunksOfInValues ) {
194
+ Specification <X > multiOrEqualSpec = Specification .anyOf (in (column , chunk ));
195
+ if (containerSpec == null ) {
196
+ containerSpec = multiOrEqualSpec ;
197
+ } else {
198
+ containerSpec = containerSpec .or (multiOrEqualSpec );
197
199
}
198
- return containerSpec ;
199
200
}
200
- return anyOf (inPossibleValues
201
- .stream ()
202
- .map (value -> SpecificationUtils .<X >equals (column , value ))
203
- .toList ()
204
- );
201
+ return containerSpec ;
205
202
}
206
203
207
204
@ NotNull
0 commit comments