35
35
36
36
import org .springframework .core .ResolvableType ;
37
37
import org .springframework .data .domain .Example ;
38
+ import org .springframework .data .domain .KeysetScrollPosition ;
38
39
import org .springframework .data .domain .OffsetScrollPosition ;
39
40
import org .springframework .data .domain .ScrollPosition ;
40
41
import org .springframework .data .domain .Sort ;
@@ -370,24 +371,29 @@ public static class Builder<T, R> {
370
371
private final CursorStrategy <ScrollPosition > cursorStrategy ;
371
372
372
373
@ Nullable
373
- private final ScrollSubrange defaultSubrange ;
374
+ private final Integer defaultScrollCount ;
375
+
376
+ @ Nullable
377
+ private final Function <Boolean , ScrollPosition > defaultScrollPosition ;
374
378
375
379
private final Sort sort ;
376
380
377
381
@ SuppressWarnings ("unchecked" )
378
382
Builder (QueryByExampleExecutor <T > executor , Class <R > domainType ) {
379
- this (executor , TypeInformation .of ((Class <T >) domainType ), domainType , null , null , Sort .unsorted ());
383
+ this (executor , TypeInformation .of ((Class <T >) domainType ), domainType , null , null , null , Sort .unsorted ());
380
384
}
381
385
382
386
Builder (QueryByExampleExecutor <T > executor , TypeInformation <T > domainType , Class <R > resultType ,
383
- @ Nullable CursorStrategy <ScrollPosition > cursorStrategy , @ Nullable ScrollSubrange defaultSubrange ,
387
+ @ Nullable CursorStrategy <ScrollPosition > cursorStrategy ,
388
+ @ Nullable Integer defaultScrollCount , @ Nullable Function <Boolean , ScrollPosition > defaultScrollPosition ,
384
389
Sort sort ) {
385
390
386
391
this .executor = executor ;
387
392
this .domainType = domainType ;
388
393
this .resultType = resultType ;
389
394
this .cursorStrategy = cursorStrategy ;
390
- this .defaultSubrange = defaultSubrange ;
395
+ this .defaultScrollCount = defaultScrollCount ;
396
+ this .defaultScrollPosition = defaultScrollPosition ;
391
397
this .sort = sort ;
392
398
}
393
399
@@ -402,8 +408,8 @@ public static class Builder<T, R> {
402
408
*/
403
409
public <P > Builder <T , P > projectAs (Class <P > projectionType ) {
404
410
Assert .notNull (projectionType , "Projection type must not be null" );
405
- return new Builder <>(this .executor , this .domainType ,
406
- projectionType , this .cursorStrategy , this .defaultSubrange , this .sort );
411
+ return new Builder <>(this .executor , this .domainType , projectionType ,
412
+ this . cursorStrategy , this .defaultScrollCount , this .defaultScrollPosition , this .sort );
407
413
}
408
414
409
415
/**
@@ -416,8 +422,26 @@ public <P> Builder<T, P> projectAs(Class<P> projectionType) {
416
422
* @since 1.2.0
417
423
*/
418
424
public Builder <T , R > cursorStrategy (@ Nullable CursorStrategy <ScrollPosition > cursorStrategy ) {
425
+ return new Builder <>(this .executor , this .domainType , this .resultType ,
426
+ cursorStrategy , this .defaultScrollCount , this .defaultScrollPosition , this .sort );
427
+ }
428
+
429
+ /**
430
+ * Configure a default scroll count to use, and function to return a default
431
+ * {@link ScrollPosition} for forward vs backward pagination.
432
+ * <p>For offset scrolling, use {@link ScrollPosition#offset()} to scroll
433
+ * from the beginning. Currently, it is not possible to go back from the end.
434
+ * <p>For keyset scrolling, use {@link ScrollPosition#keyset()} to scroll
435
+ * from the beginning, or {@link KeysetScrollPosition#reverse()} the same
436
+ * to go back from the end.
437
+ * <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
438
+ * @since 1.2.5
439
+ */
440
+ public Builder <T , R > defaultScrollSubrange (
441
+ int defaultCount , Function <Boolean , ScrollPosition > defaultPosition ) {
442
+
419
443
return new Builder <>(this .executor , this .domainType ,
420
- this .resultType , cursorStrategy , this . defaultSubrange , this .sort );
444
+ this .resultType , this . cursorStrategy , defaultCount , defaultPosition , this .sort );
421
445
}
422
446
423
447
/**
@@ -427,11 +451,16 @@ public Builder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cur
427
451
* count of 20.
428
452
* @return a new {@link Builder} instance with all previously configured
429
453
* options and {@code Sort} applied
430
- * @since 1.2.0
454
+ * @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
431
455
*/
456
+ @ SuppressWarnings ("OptionalGetWithoutIsPresent" )
457
+ @ Deprecated (since = "1.2.5" , forRemoval = true )
432
458
public Builder <T , R > defaultScrollSubrange (@ Nullable ScrollSubrange defaultSubrange ) {
433
459
return new Builder <>(this .executor , this .domainType ,
434
- this .resultType , this .cursorStrategy , defaultSubrange , this .sort );
460
+ this .resultType , this .cursorStrategy ,
461
+ (defaultSubrange != null ? defaultSubrange .count ().getAsInt () : null ),
462
+ (defaultSubrange != null ? forward -> defaultSubrange .position ().get () : null ),
463
+ this .sort );
435
464
}
436
465
437
466
/**
@@ -442,8 +471,8 @@ public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubra
442
471
*/
443
472
public Builder <T , R > sortBy (Sort sort ) {
444
473
Assert .notNull (sort , "Sort must not be null" );
445
- return new Builder <>(this .executor , this .domainType ,
446
- this .resultType , this .cursorStrategy , this .defaultSubrange , sort );
474
+ return new Builder <>(this .executor , this .domainType , this . resultType ,
475
+ this .cursorStrategy , this .defaultScrollCount , this .defaultScrollPosition , sort );
447
476
}
448
477
449
478
/**
@@ -469,7 +498,8 @@ public DataFetcher<Iterable<R>> scrollable() {
469
498
return new ScrollableEntityFetcher <>(
470
499
this .executor , this .domainType , this .resultType ,
471
500
(this .cursorStrategy != null ? this .cursorStrategy : RepositoryUtils .defaultCursorStrategy ()),
472
- (this .defaultSubrange != null ? this .defaultSubrange : RepositoryUtils .defaultScrollSubrange ()),
501
+ (this .defaultScrollCount != null ? this .defaultScrollCount : RepositoryUtils .defaultScrollCount ()),
502
+ (this .defaultScrollPosition != null ? this .defaultScrollPosition : RepositoryUtils .defaultScrollPosition ()),
473
503
this .sort );
474
504
}
475
505
@@ -516,25 +546,30 @@ public static class ReactiveBuilder<T, R> {
516
546
private final CursorStrategy <ScrollPosition > cursorStrategy ;
517
547
518
548
@ Nullable
519
- private final ScrollSubrange defaultSubrange ;
549
+ private final Integer defaultScrollCount ;
550
+
551
+ @ Nullable
552
+ private final Function <Boolean , ScrollPosition > defaultScrollPosition ;
520
553
521
554
private final Sort sort ;
522
555
523
556
@ SuppressWarnings ("unchecked" )
524
557
ReactiveBuilder (ReactiveQueryByExampleExecutor <T > executor , Class <R > domainType ) {
525
- this (executor , TypeInformation .of ((Class <T >) domainType ), domainType , null , null , Sort .unsorted ());
558
+ this (executor , TypeInformation .of ((Class <T >) domainType ), domainType , null , null , null , Sort .unsorted ());
526
559
}
527
560
528
561
ReactiveBuilder (
529
562
ReactiveQueryByExampleExecutor <T > executor , TypeInformation <T > domainType , Class <R > resultType ,
530
- @ Nullable CursorStrategy <ScrollPosition > cursorStrategy , @ Nullable ScrollSubrange defaultSubrange ,
563
+ @ Nullable CursorStrategy <ScrollPosition > cursorStrategy ,
564
+ @ Nullable Integer defaultScrollCount , @ Nullable Function <Boolean , ScrollPosition > defaultScrollPosition ,
531
565
Sort sort ) {
532
566
533
567
this .executor = executor ;
534
568
this .domainType = domainType ;
535
569
this .resultType = resultType ;
536
570
this .cursorStrategy = cursorStrategy ;
537
- this .defaultSubrange = defaultSubrange ;
571
+ this .defaultScrollCount = defaultScrollCount ;
572
+ this .defaultScrollPosition = defaultScrollPosition ;
538
573
this .sort = sort ;
539
574
}
540
575
@@ -550,7 +585,7 @@ public static class ReactiveBuilder<T, R> {
550
585
public <P > ReactiveBuilder <T , P > projectAs (Class <P > projectionType ) {
551
586
Assert .notNull (projectionType , "Projection type must not be null" );
552
587
return new ReactiveBuilder <>(this .executor , this .domainType ,
553
- projectionType , this .cursorStrategy , this .defaultSubrange , this .sort );
588
+ projectionType , this .cursorStrategy , this .defaultScrollCount , this . defaultScrollPosition , this .sort );
554
589
}
555
590
556
591
/**
@@ -563,8 +598,26 @@ public <P> ReactiveBuilder<T, P> projectAs(Class<P> projectionType) {
563
598
* @since 1.2.0
564
599
*/
565
600
public ReactiveBuilder <T , R > cursorStrategy (@ Nullable CursorStrategy <ScrollPosition > cursorStrategy ) {
601
+ return new ReactiveBuilder <>(this .executor , this .domainType , this .resultType ,
602
+ cursorStrategy , this .defaultScrollCount , this .defaultScrollPosition , this .sort );
603
+ }
604
+
605
+ /**
606
+ * Configure a default scroll count to use, and function to return a default
607
+ * {@link ScrollPosition} for forward vs backward pagination.
608
+ * <p>For offset scrolling, use {@link ScrollPosition#offset()} to scroll
609
+ * from the beginning. Currently, it is not possible to go back from the end.
610
+ * <p>For keyset scrolling, use {@link ScrollPosition#keyset()} to scroll
611
+ * from the beginning, or {@link KeysetScrollPosition#reverse()} the same
612
+ * to go back from the end.
613
+ * <p>By default a count of 20 and {@link ScrollPosition#offset()} are used.
614
+ * @since 1.2.5
615
+ */
616
+ public ReactiveBuilder <T , R > defaultScrollSubrange (
617
+ int defaultCount , Function <Boolean , ScrollPosition > defaultPosition ) {
618
+
566
619
return new ReactiveBuilder <>(this .executor , this .domainType ,
567
- this .resultType , cursorStrategy , this . defaultSubrange , this .sort );
620
+ this .resultType , this . cursorStrategy , defaultCount , defaultPosition , this .sort );
568
621
}
569
622
570
623
/**
@@ -574,11 +627,16 @@ public ReactiveBuilder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosit
574
627
* count of 20.
575
628
* @return a new {@link Builder} instance with all previously configured
576
629
* options and {@code Sort} applied
577
- * @since 1.2.0
630
+ * @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
578
631
*/
632
+ @ SuppressWarnings ("OptionalGetWithoutIsPresent" )
633
+ @ Deprecated (since = "1.2.5" , forRemoval = true )
579
634
public ReactiveBuilder <T , R > defaultScrollSubrange (@ Nullable ScrollSubrange defaultSubrange ) {
580
635
return new ReactiveBuilder <>(this .executor , this .domainType ,
581
- this .resultType , this .cursorStrategy , defaultSubrange , this .sort );
636
+ this .resultType , this .cursorStrategy ,
637
+ (defaultSubrange != null ? defaultSubrange .count ().getAsInt () : null ),
638
+ (defaultSubrange != null ? forward -> defaultSubrange .position ().get () : null ),
639
+ this .sort );
582
640
}
583
641
584
642
/**
@@ -589,8 +647,8 @@ public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defa
589
647
*/
590
648
public ReactiveBuilder <T , R > sortBy (Sort sort ) {
591
649
Assert .notNull (sort , "Sort must not be null" );
592
- return new ReactiveBuilder <>(this .executor , this .domainType ,
593
- this .resultType , this .cursorStrategy , this .defaultSubrange , sort );
650
+ return new ReactiveBuilder <>(this .executor , this .domainType , this . resultType ,
651
+ this .cursorStrategy , this .defaultScrollCount , this .defaultScrollPosition , sort );
594
652
}
595
653
596
654
/**
@@ -616,7 +674,8 @@ public DataFetcher<Mono<Iterable<R>>> scrollable() {
616
674
return new ReactiveScrollableEntityFetcher <>(
617
675
this .executor , this .domainType , this .resultType ,
618
676
(this .cursorStrategy != null ? this .cursorStrategy : RepositoryUtils .defaultCursorStrategy ()),
619
- (this .defaultSubrange != null ? this .defaultSubrange : RepositoryUtils .defaultScrollSubrange ()),
677
+ (this .defaultScrollCount != null ? this .defaultScrollCount : RepositoryUtils .defaultScrollCount ()),
678
+ (this .defaultScrollPosition != null ? this .defaultScrollPosition : RepositoryUtils .defaultScrollPosition ()),
620
679
this .sort );
621
680
}
622
681
@@ -747,23 +806,27 @@ private static class ScrollableEntityFetcher<T, R> extends ManyEntityFetcher<T,
747
806
748
807
private final CursorStrategy <ScrollPosition > cursorStrategy ;
749
808
750
- private final ScrollSubrange defaultSubrange ;
809
+ private final int defaultCount ;
810
+
811
+ private final Function <Boolean , ScrollPosition > defaultPosition ;
751
812
752
813
private final ResolvableType scrollableResultType ;
753
814
754
815
ScrollableEntityFetcher (
755
816
QueryByExampleExecutor <T > executor , TypeInformation <T > domainType , Class <R > resultType ,
756
- CursorStrategy <ScrollPosition > cursorStrategy , ScrollSubrange defaultSubrange , Sort sort ) {
817
+ CursorStrategy <ScrollPosition > cursorStrategy ,
818
+ int defaultCount ,
819
+ Function <Boolean , ScrollPosition > defaultPosition ,
820
+ Sort sort ) {
757
821
758
822
super (executor , domainType , resultType , sort );
759
823
760
824
Assert .notNull (cursorStrategy , "CursorStrategy is required" );
761
- Assert .notNull (defaultSubrange , "Default ScrollSubrange is required" );
762
- Assert .isTrue (defaultSubrange .position ().isPresent (), "Default ScrollPosition is required" );
763
- Assert .isTrue (defaultSubrange .count ().isPresent (), "Default scroll limit is required" );
825
+ Assert .notNull (defaultPosition , "'defaultPosition' is required" );
764
826
765
827
this .cursorStrategy = cursorStrategy ;
766
- this .defaultSubrange = defaultSubrange ;
828
+ this .defaultCount = defaultCount ;
829
+ this .defaultPosition = defaultPosition ;
767
830
this .scrollableResultType = ResolvableType .forClassWithGenerics (Window .class , resultType );
768
831
}
769
832
@@ -772,12 +835,12 @@ public ResolvableType getReturnType() {
772
835
return ResolvableType .forClassWithGenerics (Iterable .class , this .scrollableResultType );
773
836
}
774
837
775
- @ SuppressWarnings ("OptionalGetWithoutIsPresent" )
776
838
@ Override
777
839
protected Iterable <R > getResult (FluentQuery .FetchableFluentQuery <R > queryToUse , DataFetchingEnvironment env ) {
778
- ScrollSubrange range = RepositoryUtils .getScrollSubrange (env , this .cursorStrategy , this .defaultSubrange );
779
- int count = range .count ().getAsInt ();
780
- ScrollPosition position = range .position ().get ();
840
+ ScrollSubrange range = RepositoryUtils .getScrollSubrange (env , this .cursorStrategy );
841
+ int count = range .count ().orElse (this .defaultCount );
842
+ ScrollPosition position = (range .position ().isPresent () ?
843
+ range .position ().get () : this .defaultPosition .apply (range .forward ()));
781
844
return queryToUse .limit (count ).scroll (position );
782
845
}
783
846
@@ -891,26 +954,30 @@ private static class ReactiveScrollableEntityFetcher<T, R>
891
954
892
955
private final CursorStrategy <ScrollPosition > cursorStrategy ;
893
956
894
- private final ScrollSubrange defaultSubrange ;
957
+ private final int defaultCount ;
958
+
959
+ private final Function <Boolean , ScrollPosition > defaultPosition ;
895
960
896
961
private final Sort sort ;
897
962
898
963
ReactiveScrollableEntityFetcher (
899
964
ReactiveQueryByExampleExecutor <T > executor , TypeInformation <T > domainType , Class <R > resultType ,
900
- CursorStrategy <ScrollPosition > cursorStrategy , ScrollSubrange defaultSubrange , Sort sort ) {
965
+ CursorStrategy <ScrollPosition > cursorStrategy ,
966
+ int defaultCount ,
967
+ Function <Boolean , ScrollPosition > defaultPosition ,
968
+ Sort sort ) {
901
969
902
970
super (domainType );
903
971
904
972
Assert .notNull (cursorStrategy , "CursorStrategy is required" );
905
- Assert .notNull (defaultSubrange , "Default ScrollSubrange is required" );
906
- Assert .isTrue (defaultSubrange .position ().isPresent (), "Default ScrollPosition is required" );
907
- Assert .isTrue (defaultSubrange .count ().isPresent (), "Default scroll limit is required" );
973
+ Assert .notNull (defaultPosition , "'defaultPosition' is required" );
908
974
909
975
this .executor = executor ;
910
976
this .resultType = resultType ;
911
977
this .scrollableResultType = ResolvableType .forClassWithGenerics (Iterable .class , resultType );
912
978
this .cursorStrategy = cursorStrategy ;
913
- this .defaultSubrange = defaultSubrange ;
979
+ this .defaultCount = defaultCount ;
980
+ this .defaultPosition = defaultPosition ;
914
981
this .sort = sort ;
915
982
}
916
983
@@ -920,7 +987,7 @@ public ResolvableType getReturnType() {
920
987
}
921
988
922
989
@ Override
923
- @ SuppressWarnings ({ "unchecked" , "OptionalGetWithoutIsPresent" } )
990
+ @ SuppressWarnings ("unchecked" )
924
991
public Mono <Iterable <R >> get (DataFetchingEnvironment env ) throws BindException {
925
992
return this .executor .findBy (buildExample (env ), query -> {
926
993
FluentQuery .ReactiveFluentQuery <R > queryToUse = (FluentQuery .ReactiveFluentQuery <R >) query ;
@@ -936,9 +1003,10 @@ public Mono<Iterable<R>> get(DataFetchingEnvironment env) throws BindException {
936
1003
queryToUse = queryToUse .project (buildPropertyPaths (env .getSelectionSet (), this .resultType ));
937
1004
}
938
1005
939
- ScrollSubrange range = RepositoryUtils .getScrollSubrange (env , this .cursorStrategy , this .defaultSubrange );
940
- int count = range .count ().getAsInt ();
941
- ScrollPosition position = range .position ().get ();
1006
+ ScrollSubrange range = RepositoryUtils .getScrollSubrange (env , this .cursorStrategy );
1007
+ int count = range .count ().orElse (this .defaultCount );
1008
+ ScrollPosition position = (range .position ().isPresent () ?
1009
+ range .position ().get () : this .defaultPosition .apply (range .forward ()));
942
1010
return queryToUse .limit (count ).scroll (position ).map (Function .identity ());
943
1011
});
944
1012
}
0 commit comments