Vaadin CallbackDataProvider 如何进行异步获取数据?

How to Vaadin CallbackDataProvider make an asyncronous fetch data?

Vaadin 14.CallbackDataProvider。当服务缓慢且应答时间较长时,连接到 CallbackDataProvider 的网格将全部冻结 UI。 一些例子:

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.provider.CallbackDataProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouteAlias;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

public class GridWithSortableColumnsView extends VerticalLayout {

    public GridWithSortableColumnsView() {
        addClassName("dialogwithgrid-view");
        setSizeFull();

        // prepare Data
        List<GridRow> gridRows = new LinkedList<>();
        for (int i = 0; i < 10000; i++) {
            gridRows.add(new GridRow(i, RandomStringUtils.randomAlphanumeric(10)));
        }

        CallbackDataProvider<GridRow, Void> dataProvider = new CallbackDataProvider<>(
                query -> {

                    // Slow service answer is here
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    Notification.show("Offset=" + query.getOffset() + " Limit=" + query.getLimit());
                    return gridRows.stream()
                            .sorted((o1, o2) -> StringUtils.compare(o1.getCol05(), o2.getCol05()))
                            .skip(query.getOffset())
                            .limit(query.getLimit());
                },
                query -> gridRows.size());

        Grid<GridRow> grid = new Grid<>();
        grid.setPageSize(10);
        grid.setSizeFull();
        grid.addColumn(GridRow::getId).setHeader("Id");
        grid.addColumn(GridRow::getCol05).setHeader("col05");
        grid.setDataProvider(dataProvider);

        add(grid);
    }


    @EqualsAndHashCode(onlyExplicitlyIncluded = true)
    @AllArgsConstructor
    @Getter
    @Setter
    public static class GridRow {
        @EqualsAndHashCode.Include
        private int id;
        private String col05;
    }

}

会话锁定,所有 UI 组件在滚动此网格时冻结。 它如何更快和异步?我考虑在自己的 DataProvider 中缓存 extends CallbackDataProvider,但它会在 Grid 在第一次请求时收到空数据后填充,并且只能在一段时间后再次刷新 DataProvider 才能使用。这对我没有用。在我的实际项目中,获取的数据经常变化并且有大量的项目,一个网格最多 100000 个项目,所以我需要使用请求大小有限的 CallbackDataProvider。

Vaadin 23:

在 Vaadin 23 中现在有一个新功能,它允许您为 DataCommunicator 定义执行器以用于与 Grid 的异步操作。如果该值为 null,DataCommunicator 将同步运行,这是默认值。如果定义了执行器,执行器将获取回调 运行,并使用 UI#access(..) 更新网格。这需要启用推送。

grid.getDataCommunicator().enablePushUpdates(Executors.newCachedThreadPool());

Vaadin 14:

可以尝试使用缓存来减少查询时间。您可以使用像 ehcache 这样的通用缓存,也可以将缓存集成到您的数据提供程序中。它是特定于应用程序的,哪个更适合您,全局缓存还是本地缓存。

如果查询仍然需要那么长时间,那么我会为您提出替代方法 UI。不使用回调数据提供程序,而是将 Grid 与内存数据提供程序一起使用,但不要立即将整个数据加载到数据提供程序。而是创建分页视图。当用户点击时查询新数据,例如“下一个”/“上一个”等按钮。并在查询完成时使用 UI#access 异步方法更新 UI。

您可以使用专用于 searching/filtering 的 附加 数据库/搜索引擎,它可以非常快速地为您的网格提供数据。 Elasticsearch 是该用例的不错选择。

搜索引擎仅适用于您的网格。对于所有其他用例(输入表单、处理等),您进一步使用当前数据库。

当然要实现更新机制。每当当前数据库中的数据发生变化时,您都必须通知 Elasticsearch 系统并在那里更新数据。