2626 * @author Kevin Le Saulnier <kevin.lesaulnier@rte-france.com>
2727 */
2828public 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 ;
3429
3530 public static final String FIELD_SEPARATOR = "." ;
3631
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+
3737 // Utility class, so no constructor
3838 private SpecificationUtils () { }
3939
@@ -51,6 +51,11 @@ public static <X> Specification<X> notEqual(String field, String value) {
5151 return (root , cq , cb ) -> cb .notEqual (getColumnPath (root , field ), value );
5252 }
5353
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+
5459 public static <X > Specification <X > contains (String field , String value ) {
5560 return (root , cq , cb ) -> cb .like (cb .upper (getColumnPath (root , field ).as (String .class )), "%" + EscapeCharacter .DEFAULT .escape (value ).toUpperCase () + "%" , EscapeCharacter .DEFAULT .getEscapeCharacter ());
5661 }
@@ -135,15 +140,22 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio
135140 // implicitly an IN resourceFilter type because only IN may have value lists as filter value
136141 List <String > inValues = valueList .stream ()
137142 .map (Object ::toString )
143+ .map (String ::toUpperCase )
138144 .toList ();
139145 completedSpecification = completedSpecification .and (
146+ resourceFilter .type () == ResourceFilterDTO .Type .NOT_EQUAL ?
147+ not (generateInSpecification (resourceFilter .column (), inValues )) :
140148 generateInSpecification (resourceFilter .column (), inValues )
141149 );
142150 } else if (resourceFilter .value () == null ) {
143151 // if the value is null, we build an impossible specification (trick to remove later on ?)
144152 completedSpecification = completedSpecification .and (not (completedSpecification ));
145153 } 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+ );
147159 }
148160 }
149161 case CONTAINS -> {
@@ -176,32 +188,17 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio
176188 * @return a specification for the IN clause
177189 */
178190 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 );
197199 }
198- return containerSpec ;
199200 }
200- return anyOf (inPossibleValues
201- .stream ()
202- .map (value -> SpecificationUtils .<X >equals (column , value ))
203- .toList ()
204- );
201+ return containerSpec ;
205202 }
206203
207204 @ NotNull
0 commit comments