Skip to content

Commit 689a3d5

Browse files
Artur-sissbruecker
andauthoredJan 15, 2025
feat: Spring Data API for Grid (#7011)
* feat: Spring Data API for Grid Provides a Spring Data specific setItems variant based on Pageable. The method is called setItemsSpring because the signature is the same as existing setItems methods. Used as e.g. ``` grid.setItemsSpring(pageable -> productService.list(pageable)) ``` --------- Co-authored-by: Sascha Ißbrücker <[email protected]>
1 parent 6fa4091 commit 689a3d5

File tree

7 files changed

+229
-1
lines changed

7 files changed

+229
-1
lines changed
 

‎pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
<cvdlName/>
8888
<jakarta.servlet.api.version>5.0.0</jakarta.servlet.api.version>
8989
<jakarta.web.api.version>10.0.0</jakarta.web.api.version>
90+
<spring-data-commons.version>3.4.1</spring-data-commons.version>
9091

9192
<!-- spreadsheet -->
9293
<poi.version>5.2.5</poi.version>

‎vaadin-crud-flow-parent/vaadin-crud-flow/pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@
7878
<artifactId>slf4j-simple</artifactId>
7979
<scope>test</scope>
8080
</dependency>
81+
<!-- Optional Spring dependencies to be able to provide Spring Data API-->
82+
<dependency>
83+
<groupId>org.springframework.data</groupId>
84+
<artifactId>spring-data-commons</artifactId>
85+
<version>${spring-data-commons.version}</version>
86+
<scope>provided</scope>
87+
</dependency>
8188
</dependencies>
8289
<build>
8390
<plugins>

‎vaadin-grid-flow-parent/vaadin-grid-flow/pom.xml

+14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<artifactId>flow-html-components</artifactId>
3535
<scope>provided</scope>
3636
</dependency>
37+
3738
<dependency>
3839
<groupId>com.vaadin</groupId>
3940
<artifactId>flow-test-generic</artifactId>
@@ -60,6 +61,12 @@
6061
<artifactId>vaadin-renderer-flow</artifactId>
6162
<version>${project.version}</version>
6263
</dependency>
64+
<dependency>
65+
<groupId>com.vaadin</groupId>
66+
<artifactId>vaadin-spring</artifactId>
67+
<version>${flow.version}</version>
68+
<optional>true</optional>
69+
</dependency>
6370
<dependency>
6471
<groupId>com.vaadin</groupId>
6572
<artifactId>vaadin-text-field-flow</artifactId>
@@ -89,6 +96,13 @@
8996
<artifactId>slf4j-simple</artifactId>
9097
<scope>test</scope>
9198
</dependency>
99+
<!-- Optional Spring dependencies to be able to provide Spring Data API-->
100+
<dependency>
101+
<groupId>org.springframework.data</groupId>
102+
<artifactId>spring-data-commons</artifactId>
103+
<version>${spring-data-commons.version}</version>
104+
<optional>true</optional>
105+
</dependency>
92106
</dependencies>
93107
<build>
94108
<plugins>

‎vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/Grid.java

+126
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.stream.Stream;
3737

3838
import org.slf4j.LoggerFactory;
39+
import org.springframework.data.domain.Pageable;
3940

4041
import com.vaadin.flow.component.AttachEvent;
4142
import com.vaadin.flow.component.ClientCallable;
@@ -131,6 +132,7 @@
131132
import com.vaadin.flow.internal.JsonUtils;
132133
import com.vaadin.flow.internal.ReflectTools;
133134
import com.vaadin.flow.shared.Registration;
135+
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
134136

135137
import elemental.json.Json;
136138
import elemental.json.JsonArray;
@@ -2916,6 +2918,130 @@ public GridLazyDataView<T> setItems(
29162918
return getLazyDataView();
29172919
}
29182920

2921+
public interface SpringData extends Serializable {
2922+
/**
2923+
* Callback interface for fetching a list of items from a backend based
2924+
* on a Spring Data Pageable.
2925+
*
2926+
* @param <T>
2927+
* the type of the items to fetch
2928+
*/
2929+
@FunctionalInterface
2930+
public interface FetchCallback<PAGEABLE, T> extends Serializable {
2931+
2932+
/**
2933+
* Fetches a list of items based on a pageable. The pageable defines
2934+
* the paging of the items to fetch and the sorting.
2935+
*
2936+
* @param pageable
2937+
* the pageable that defines which items to fetch and the
2938+
* sort order
2939+
* @return a list of items
2940+
*/
2941+
List<T> fetch(PAGEABLE pageable);
2942+
}
2943+
2944+
/**
2945+
* Callback interface for counting the number of items in a backend
2946+
* based on a Spring Data Pageable.
2947+
*/
2948+
@FunctionalInterface
2949+
public interface CountCallback<PAGEABLE> extends Serializable {
2950+
/**
2951+
* Counts the number of available items based on a pageable. The
2952+
* pageable defines the paging of the items to fetch and the sorting
2953+
* and is provided although it is generally not needed for
2954+
* determining the number of items.
2955+
*
2956+
* @param pageable
2957+
* the pageable that defines which items to fetch and the
2958+
* sort order
2959+
* @return the number of available items
2960+
*/
2961+
long count(PAGEABLE pageable);
2962+
}
2963+
}
2964+
2965+
/**
2966+
* Supply items lazily with a callback from a backend based on a Spring Data
2967+
* Pageable. The component will automatically fetch more items and adjust
2968+
* its size until the backend runs out of items. Usage example:
2969+
* <p>
2970+
* {@code component.setItemsPageable(pageable -> orderService.getOrders(pageable));}
2971+
* <p>
2972+
* The returned data view object can be used for further configuration, or
2973+
* later on fetched with {@link #getLazyDataView()}. For using in-memory
2974+
* data, like {@link java.util.Collection}, use
2975+
* {@link HasListDataView#setItems(Collection)} instead.
2976+
*
2977+
* @param fetchCallback
2978+
* a function that returns a sorted list of items from the
2979+
* backend based on the given pageable
2980+
* @return a data view for further configuration
2981+
*/
2982+
public GridLazyDataView<T> setItemsPageable(
2983+
SpringData.FetchCallback<Pageable, T> fetchCallback) {
2984+
return setItems(
2985+
query -> handleSpringFetchCallback(query, fetchCallback));
2986+
}
2987+
2988+
/**
2989+
* Supply items lazily with callbacks: the first one fetches a list of items
2990+
* from a backend based on a Spring Data Pageable, the second provides the
2991+
* exact count of items in the backend. Use this in case getting the count
2992+
* is cheap and the user benefits from the component showing immediately the
2993+
* exact size. Usage example:
2994+
* <p>
2995+
* {@code component.setItemsPageable(
2996+
* pageable -> orderService.getOrders(pageable),
2997+
* pageable -> orderService.countOrders());}
2998+
* <p>
2999+
* The returned data view object can be used for further configuration, or
3000+
* later on fetched with {@link #getLazyDataView()}. For using in-memory
3001+
* data, like {@link java.util.Collection}, use
3002+
* {@link HasListDataView#setItems(Collection)} instead.
3003+
*
3004+
* @param fetchCallback
3005+
* a function that returns a sorted list of items from the
3006+
* backend based on the given pageable
3007+
* @param countCallback
3008+
* a function that returns the number of items in the back end
3009+
* @return LazyDataView instance for further configuration
3010+
*/
3011+
public GridLazyDataView<T> setItemsPageable(
3012+
SpringData.FetchCallback<Pageable, T> fetchCallback,
3013+
SpringData.CountCallback<Pageable> countCallback) {
3014+
return setItems(
3015+
query -> handleSpringFetchCallback(query, fetchCallback),
3016+
query -> handleSpringCountCallback(query, countCallback));
3017+
}
3018+
3019+
@SuppressWarnings("unchecked")
3020+
private static <PAGEABLE, T> Stream<T> handleSpringFetchCallback(
3021+
Query<T, Void> query,
3022+
SpringData.FetchCallback<PAGEABLE, T> fetchCallback) {
3023+
PAGEABLE pageable = (PAGEABLE) VaadinSpringDataHelpers
3024+
.toSpringPageRequest(query);
3025+
List<T> itemList = fetchCallback.fetch(pageable);
3026+
return itemList.stream();
3027+
}
3028+
3029+
@SuppressWarnings("unchecked")
3030+
private static <PAGEABLE> int handleSpringCountCallback(
3031+
Query<?, Void> query,
3032+
SpringData.CountCallback<PAGEABLE> countCallback) {
3033+
PAGEABLE pageable = (PAGEABLE) VaadinSpringDataHelpers
3034+
.toSpringPageRequest(query);
3035+
long count = countCallback.count(pageable);
3036+
if (count > Integer.MAX_VALUE) {
3037+
LoggerFactory.getLogger(Grid.class).warn(
3038+
"The count of items in the backend ({}) exceeds the maximum supported by the Grid.",
3039+
count);
3040+
return Integer.MAX_VALUE;
3041+
}
3042+
return (int) count;
3043+
}
3044+
29193045
/**
29203046
* Gets the lazy data view for the grid. This data view should only be used
29213047
* when the items are provided lazily from the backend with:

‎vaadin-grid-flow-parent/vaadin-grid-flow/src/test/java/com/vaadin/flow/component/grid/GridSerializableTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ protected Stream<String> getExcludedPatterns() {
3636
"com\\.vaadin\\.flow\\.component\\.contextmenu\\.osgi\\..*",
3737
"com\\.vaadin\\.flow\\.component\\.treegrid\\.it\\..*",
3838
"com\\.vaadin\\.flow\\.component\\.datepicker\\..*",
39-
"com\\.vaadin\\.flow\\.component\\.grid\\.GridColumnOrderHelper.*"));
39+
"com\\.vaadin\\.flow\\.component\\.grid\\.GridColumnOrderHelper.*",
40+
"com\\.vaadin\\.flow\\.spring\\..*"));
4041
}
4142

4243
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.grid;
17+
18+
import java.util.List;
19+
import java.util.concurrent.atomic.AtomicInteger;
20+
21+
import org.junit.Assert;
22+
import org.junit.Test;
23+
24+
public class GridSpringDataTest {
25+
26+
@Test
27+
public void setItemsPageableNoCount() {
28+
AtomicInteger pageSize = new AtomicInteger(-1);
29+
AtomicInteger pageNumber = new AtomicInteger(-1);
30+
Grid<Person> grid = new Grid<>(Person.class);
31+
grid.setItemsPageable(pageable -> {
32+
if (pageSize.get() != -1) {
33+
throw new IllegalStateException(
34+
"There should be only one call to the data provider");
35+
}
36+
pageSize.set(pageable.getPageSize());
37+
pageNumber.set(pageable.getPageNumber());
38+
39+
return List.of(new Person("John", 1293));
40+
});
41+
42+
Person item = grid.getLazyDataView().getItem(0);
43+
44+
Assert.assertEquals(0, pageNumber.get());
45+
Assert.assertTrue(pageSize.get() > 0);
46+
Assert.assertEquals("John", item.getName());
47+
}
48+
49+
@Test
50+
public void setItemsPageableWithCount() {
51+
AtomicInteger pageSize = new AtomicInteger(-1);
52+
AtomicInteger pageNumber = new AtomicInteger(-1);
53+
Grid<Person> grid = new Grid<>(Person.class);
54+
grid.setItemsPageable(pageable -> {
55+
if (pageSize.get() != -1) {
56+
throw new IllegalStateException(
57+
"There should be only one call to the data provider");
58+
}
59+
pageSize.set(pageable.getPageSize());
60+
pageNumber.set(pageable.getPageNumber());
61+
62+
return List.of(new Person("John", 1293), new Person("Jane", 1923),
63+
new Person("Homer", 1956));
64+
}, pageable -> 3L);
65+
66+
Person item = grid.getLazyDataView().getItems().toList().get(1);
67+
68+
Assert.assertEquals(0, pageNumber.get());
69+
Assert.assertEquals(3, pageSize.get());
70+
Assert.assertEquals("Jane", item.getName());
71+
}
72+
}

‎vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@
6363
<artifactId>slf4j-simple</artifactId>
6464
<scope>test</scope>
6565
</dependency>
66+
<!-- Optional Spring dependencies to be able to provide Spring Data API-->
67+
<dependency>
68+
<groupId>org.springframework.data</groupId>
69+
<artifactId>spring-data-commons</artifactId>
70+
<version>${spring-data-commons.version}</version>
71+
<scope>provided</scope>
72+
</dependency>
6673
</dependencies>
6774
<build>
6875
<plugins>

0 commit comments

Comments
 (0)
Please sign in to comment.