@@ -229,6 +229,30 @@ public static Direction fromStringOrNull(String value) {
229
229
}
230
230
}
231
231
232
+ /**
233
+ * Enumeration for null handling hints that can be used in {@link Order} expressions.
234
+ *
235
+ * @author Thomas Darimont
236
+ * @since 1.7
237
+ */
238
+ public static enum NullHandling {
239
+
240
+ /**
241
+ * Lets the data store decide what to do with nulls.
242
+ */
243
+ NATIVE , //
244
+
245
+ /**
246
+ * A hint to the used data store to order entries with null values before non null entries.
247
+ */
248
+ NULLS_FIRST , //
249
+
250
+ /**
251
+ * A hint to the used data store to order entries with null values after non null entries.
252
+ */
253
+ NULLS_LAST ; //
254
+ }
255
+
232
256
/**
233
257
* PropertyPath implements the pairing of an {@link Direction} and a property. It is used to provide input for
234
258
* {@link Sort}
@@ -244,6 +268,7 @@ public static class Order implements Serializable {
244
268
private final Direction direction ;
245
269
private final String property ;
246
270
private final boolean ignoreCase ;
271
+ private final NullHandling nullHandlingHint ;
247
272
248
273
/**
249
274
* Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
@@ -254,7 +279,20 @@ public static class Order implements Serializable {
254
279
*/
255
280
public Order (Direction direction , String property ) {
256
281
257
- this (direction , property , DEFAULT_IGNORE_CASE );
282
+ this (direction , property , DEFAULT_IGNORE_CASE , null );
283
+ }
284
+
285
+ /**
286
+ * Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
287
+ * {@link Sort#DEFAULT_DIRECTION}
288
+ *
289
+ * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
290
+ * @param property must not be {@literal null} or empty.
291
+ * @param nullHandlingHint can be {@literal null}, will default to {@link NullHandling#NATIVE}.
292
+ */
293
+ public Order (Direction direction , String property , NullHandling nullHandlingHint ) {
294
+
295
+ this (direction , property , DEFAULT_IGNORE_CASE , nullHandlingHint );
258
296
}
259
297
260
298
/**
@@ -274,8 +312,10 @@ public Order(String property) {
274
312
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
275
313
* @param property must not be {@literal null} or empty.
276
314
* @param ignoreCase true if sorting should be case insensitive. false if sorting should be case sensitive.
315
+ * @param nullHandlingHint can be {@literal null}, will default to {@link NullHandling#NATIVE}.
316
+ * @since 1.7
277
317
*/
278
- private Order (Direction direction , String property , boolean ignoreCase ) {
318
+ private Order (Direction direction , String property , boolean ignoreCase , NullHandling nullHandlingHint ) {
279
319
280
320
if (!StringUtils .hasText (property )) {
281
321
throw new IllegalArgumentException ("Property must not null or empty!" );
@@ -284,6 +324,7 @@ private Order(Direction direction, String property, boolean ignoreCase) {
284
324
this .direction = direction == null ? DEFAULT_DIRECTION : direction ;
285
325
this .property = property ;
286
326
this .ignoreCase = ignoreCase ;
327
+ this .nullHandlingHint = nullHandlingHint == null ? NullHandling .NATIVE : nullHandlingHint ;
287
328
}
288
329
289
330
/**
@@ -342,7 +383,7 @@ public boolean isIgnoreCase() {
342
383
* @return
343
384
*/
344
385
public Order with (Direction order ) {
345
- return new Order (order , this .property );
386
+ return new Order (order , this .property , nullHandlingHint );
346
387
}
347
388
348
389
/**
@@ -361,7 +402,58 @@ public Sort withProperties(String... properties) {
361
402
* @return
362
403
*/
363
404
public Order ignoreCase () {
364
- return new Order (direction , property , true );
405
+ return new Order (direction , property , true , nullHandlingHint );
406
+ }
407
+
408
+ /**
409
+ * Returns a {@link Order} with the given {@link NullHandling}.
410
+ *
411
+ * @param nullHandling
412
+ * @return
413
+ * @since 1.7
414
+ */
415
+ public Order withNullHandling (NullHandling nullHandling ) {
416
+ return new Order (direction , this .property , ignoreCase , nullHandling );
417
+ }
418
+
419
+ /**
420
+ * Returns a {@link Order} with {@link NullHandling#NULLS_FIRST} as null handling hint.
421
+ *
422
+ * @return
423
+ * @since 1.7
424
+ */
425
+ public Order nullsFirst () {
426
+ return withNullHandling (NullHandling .NULLS_FIRST );
427
+ }
428
+
429
+ /**
430
+ * Returns a {@link Order} with {@link NullHandling#NULLS_LAST} as null handling hint.
431
+ *
432
+ * @return
433
+ * @since 1.7
434
+ */
435
+ public Order nullsLast () {
436
+ return withNullHandling (NullHandling .NULLS_LAST );
437
+ }
438
+
439
+ /**
440
+ * Returns a {@link Order} with {@link NullHandling#NATIVE} as null handling hint.
441
+ *
442
+ * @return
443
+ * @since 1.7
444
+ */
445
+ public Order nullsNative () {
446
+ return withNullHandling (NullHandling .NATIVE );
447
+ }
448
+
449
+ /**
450
+ * Returns the used {@link NullHandling} hint, which can but may not be respected by the used datastore.
451
+ *
452
+ * @return
453
+ * @since 1.7
454
+ */
455
+ public NullHandling getNullHandlingHint () {
456
+ return nullHandlingHint ;
365
457
}
366
458
367
459
/*
@@ -376,6 +468,7 @@ public int hashCode() {
376
468
result = 31 * result + direction .hashCode ();
377
469
result = 31 * result + property .hashCode ();
378
470
result = 31 * result + (ignoreCase ? 1 : 0 );
471
+ result = 31 * result + (nullHandlingHint .hashCode ());
379
472
380
473
return result ;
381
474
}
@@ -398,7 +491,7 @@ public boolean equals(Object obj) {
398
491
Order that = (Order ) obj ;
399
492
400
493
return this .direction .equals (that .direction ) && this .property .equals (that .property )
401
- && this .ignoreCase == that .ignoreCase ;
494
+ && this .ignoreCase == that .ignoreCase && this . nullHandlingHint . equals ( that . nullHandlingHint ) ;
402
495
}
403
496
404
497
/*
@@ -409,7 +502,16 @@ public boolean equals(Object obj) {
409
502
public String toString () {
410
503
411
504
String result = String .format ("%s: %s" , property , direction );
412
- return ignoreCase ? result .concat (", ignoring case" ) : result ;
505
+
506
+ if (!NullHandling .NATIVE .equals (nullHandlingHint )) {
507
+ result += ", " + nullHandlingHint ;
508
+ }
509
+
510
+ if (ignoreCase ) {
511
+ result += ", ignoring case" ;
512
+ }
513
+
514
+ return result ;
413
515
}
414
516
}
415
517
}
0 commit comments