3535
3636import org .springframework .core .ResolvableType ;
3737import org .springframework .data .domain .Example ;
38+ import org .springframework .data .domain .KeysetScrollPosition ;
3839import org .springframework .data .domain .OffsetScrollPosition ;
3940import org .springframework .data .domain .ScrollPosition ;
4041import org .springframework .data .domain .Sort ;
@@ -370,24 +371,29 @@ public static class Builder<T, R> {
370371 private final CursorStrategy <ScrollPosition > cursorStrategy ;
371372
372373 @ Nullable
373- private final ScrollSubrange defaultSubrange ;
374+ private final Integer defaultScrollCount ;
375+
376+ @ Nullable
377+ private final Function <Boolean , ScrollPosition > defaultScrollPosition ;
374378
375379 private final Sort sort ;
376380
377381 @ SuppressWarnings ("unchecked" )
378382 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 ());
380384 }
381385
382386 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 ,
384389 Sort sort ) {
385390
386391 this .executor = executor ;
387392 this .domainType = domainType ;
388393 this .resultType = resultType ;
389394 this .cursorStrategy = cursorStrategy ;
390- this .defaultSubrange = defaultSubrange ;
395+ this .defaultScrollCount = defaultScrollCount ;
396+ this .defaultScrollPosition = defaultScrollPosition ;
391397 this .sort = sort ;
392398 }
393399
@@ -402,8 +408,8 @@ public static class Builder<T, R> {
402408 */
403409 public <P > Builder <T , P > projectAs (Class <P > projectionType ) {
404410 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 );
407413 }
408414
409415 /**
@@ -416,8 +422,26 @@ public <P> Builder<T, P> projectAs(Class<P> projectionType) {
416422 * @since 1.2.0
417423 */
418424 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+
419443 return new Builder <>(this .executor , this .domainType ,
420- this .resultType , cursorStrategy , this . defaultSubrange , this .sort );
444+ this .resultType , this . cursorStrategy , defaultCount , defaultPosition , this .sort );
421445 }
422446
423447 /**
@@ -427,11 +451,16 @@ public Builder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosition> cur
427451 * count of 20.
428452 * @return a new {@link Builder} instance with all previously configured
429453 * options and {@code Sort} applied
430- * @since 1.2.0
454+ * @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
431455 */
456+ @ SuppressWarnings ("OptionalGetWithoutIsPresent" )
457+ @ Deprecated (since = "1.2.5" , forRemoval = true )
432458 public Builder <T , R > defaultScrollSubrange (@ Nullable ScrollSubrange defaultSubrange ) {
433459 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 );
435464 }
436465
437466 /**
@@ -442,8 +471,8 @@ public Builder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defaultSubra
442471 */
443472 public Builder <T , R > sortBy (Sort sort ) {
444473 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 );
447476 }
448477
449478 /**
@@ -469,7 +498,8 @@ public DataFetcher<Iterable<R>> scrollable() {
469498 return new ScrollableEntityFetcher <>(
470499 this .executor , this .domainType , this .resultType ,
471500 (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 ()),
473503 this .sort );
474504 }
475505
@@ -516,25 +546,30 @@ public static class ReactiveBuilder<T, R> {
516546 private final CursorStrategy <ScrollPosition > cursorStrategy ;
517547
518548 @ Nullable
519- private final ScrollSubrange defaultSubrange ;
549+ private final Integer defaultScrollCount ;
550+
551+ @ Nullable
552+ private final Function <Boolean , ScrollPosition > defaultScrollPosition ;
520553
521554 private final Sort sort ;
522555
523556 @ SuppressWarnings ("unchecked" )
524557 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 ());
526559 }
527560
528561 ReactiveBuilder (
529562 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 ,
531565 Sort sort ) {
532566
533567 this .executor = executor ;
534568 this .domainType = domainType ;
535569 this .resultType = resultType ;
536570 this .cursorStrategy = cursorStrategy ;
537- this .defaultSubrange = defaultSubrange ;
571+ this .defaultScrollCount = defaultScrollCount ;
572+ this .defaultScrollPosition = defaultScrollPosition ;
538573 this .sort = sort ;
539574 }
540575
@@ -550,7 +585,7 @@ public static class ReactiveBuilder<T, R> {
550585 public <P > ReactiveBuilder <T , P > projectAs (Class <P > projectionType ) {
551586 Assert .notNull (projectionType , "Projection type must not be null" );
552587 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 );
554589 }
555590
556591 /**
@@ -563,8 +598,26 @@ public <P> ReactiveBuilder<T, P> projectAs(Class<P> projectionType) {
563598 * @since 1.2.0
564599 */
565600 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+
566619 return new ReactiveBuilder <>(this .executor , this .domainType ,
567- this .resultType , cursorStrategy , this . defaultSubrange , this .sort );
620+ this .resultType , this . cursorStrategy , defaultCount , defaultPosition , this .sort );
568621 }
569622
570623 /**
@@ -574,11 +627,16 @@ public ReactiveBuilder<T, R> cursorStrategy(@Nullable CursorStrategy<ScrollPosit
574627 * count of 20.
575628 * @return a new {@link Builder} instance with all previously configured
576629 * options and {@code Sort} applied
577- * @since 1.2.0
630+ * @deprecated in favor of {@link #defaultScrollSubrange(int, Function)}
578631 */
632+ @ SuppressWarnings ("OptionalGetWithoutIsPresent" )
633+ @ Deprecated (since = "1.2.5" , forRemoval = true )
579634 public ReactiveBuilder <T , R > defaultScrollSubrange (@ Nullable ScrollSubrange defaultSubrange ) {
580635 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 );
582640 }
583641
584642 /**
@@ -589,8 +647,8 @@ public ReactiveBuilder<T, R> defaultScrollSubrange(@Nullable ScrollSubrange defa
589647 */
590648 public ReactiveBuilder <T , R > sortBy (Sort sort ) {
591649 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 );
594652 }
595653
596654 /**
@@ -616,7 +674,8 @@ public DataFetcher<Mono<Iterable<R>>> scrollable() {
616674 return new ReactiveScrollableEntityFetcher <>(
617675 this .executor , this .domainType , this .resultType ,
618676 (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 ()),
620679 this .sort );
621680 }
622681
@@ -747,23 +806,27 @@ private static class ScrollableEntityFetcher<T, R> extends ManyEntityFetcher<T,
747806
748807 private final CursorStrategy <ScrollPosition > cursorStrategy ;
749808
750- private final ScrollSubrange defaultSubrange ;
809+ private final int defaultCount ;
810+
811+ private final Function <Boolean , ScrollPosition > defaultPosition ;
751812
752813 private final ResolvableType scrollableResultType ;
753814
754815 ScrollableEntityFetcher (
755816 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 ) {
757821
758822 super (executor , domainType , resultType , sort );
759823
760824 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" );
764826
765827 this .cursorStrategy = cursorStrategy ;
766- this .defaultSubrange = defaultSubrange ;
828+ this .defaultCount = defaultCount ;
829+ this .defaultPosition = defaultPosition ;
767830 this .scrollableResultType = ResolvableType .forClassWithGenerics (Window .class , resultType );
768831 }
769832
@@ -772,12 +835,12 @@ public ResolvableType getReturnType() {
772835 return ResolvableType .forClassWithGenerics (Iterable .class , this .scrollableResultType );
773836 }
774837
775- @ SuppressWarnings ("OptionalGetWithoutIsPresent" )
776838 @ Override
777839 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 ()));
781844 return queryToUse .limit (count ).scroll (position );
782845 }
783846
@@ -891,26 +954,30 @@ private static class ReactiveScrollableEntityFetcher<T, R>
891954
892955 private final CursorStrategy <ScrollPosition > cursorStrategy ;
893956
894- private final ScrollSubrange defaultSubrange ;
957+ private final int defaultCount ;
958+
959+ private final Function <Boolean , ScrollPosition > defaultPosition ;
895960
896961 private final Sort sort ;
897962
898963 ReactiveScrollableEntityFetcher (
899964 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 ) {
901969
902970 super (domainType );
903971
904972 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" );
908974
909975 this .executor = executor ;
910976 this .resultType = resultType ;
911977 this .scrollableResultType = ResolvableType .forClassWithGenerics (Iterable .class , resultType );
912978 this .cursorStrategy = cursorStrategy ;
913- this .defaultSubrange = defaultSubrange ;
979+ this .defaultCount = defaultCount ;
980+ this .defaultPosition = defaultPosition ;
914981 this .sort = sort ;
915982 }
916983
@@ -920,7 +987,7 @@ public ResolvableType getReturnType() {
920987 }
921988
922989 @ Override
923- @ SuppressWarnings ({ "unchecked" , "OptionalGetWithoutIsPresent" } )
990+ @ SuppressWarnings ("unchecked" )
924991 public Mono <Iterable <R >> get (DataFetchingEnvironment env ) throws BindException {
925992 return this .executor .findBy (buildExample (env ), query -> {
926993 FluentQuery .ReactiveFluentQuery <R > queryToUse = (FluentQuery .ReactiveFluentQuery <R >) query ;
@@ -936,9 +1003,10 @@ public Mono<Iterable<R>> get(DataFetchingEnvironment env) throws BindException {
9361003 queryToUse = queryToUse .project (buildPropertyPaths (env .getSelectionSet (), this .resultType ));
9371004 }
9381005
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 ()));
9421010 return queryToUse .limit (count ).scroll (position ).map (Function .identity ());
9431011 });
9441012 }
0 commit comments