Firebase with Vaadin 14 Flow - 数据提供商从未调用过 getLimit()



我正试图将Vaadin 14表中的数据存储在firebase实时数据库中。我可以调用setValuesAsync,但在数据库中的firebase控制台中看不到任何结果。当我重新加载页面时;数据提供程序从未调用过getLimit(("。我试图将我的代码与";https://github.com/Artur-/vaadin-on-fire",但他们也不会在任何地方调用该函数。这是我的代码:TableView.java

package com.example.application.views.table;
import com.example.application.data.FirebaseDataProvider;
import com.example.application.data.db.Firebase;
import com.example.application.data.db.RunDB;
import com.example.application.data.entity.Run;
import com.example.application.data.service.SampleRunService;
import com.example.application.views.MainLayout;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasStyle;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.dependency.Uses;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridVariant;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.splitlayout.SplitLayout;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Locale;
import java.util.Optional;
import com.vaadin.ui.DateField;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.artur.helpers.CrudServiceDataProvider;
@PageTitle("Table")
@Route(value = "table/:samplePersonID?/:action?(edit)", layout = MainLayout.class)
@Uses(Icon.class)
public class TableView extends Div implements BeforeEnterObserver {
private final String RUN_ID = "samplePersonID";
private final String RUN_EDIT_ROUTE_TEMPLATE = "table/%d/edit";
private Grid<Run> grid = new Grid<>(Run.class);
private TextField name;
private NumberField distance;
private NumberField time;
private DatePicker date;
private Button cancel = new Button("Abbrechen");
private Button save = new Button("Speichern");
private Button delete = new Button("Löschen");
private BeanValidationBinder<Run> binder;
private Run run;
private SampleRunService sampleRunService;
private FirebaseDataProvider<Run> dataProvider;
public TableView(@Autowired SampleRunService sampleRunService) {
try {
Firebase.setup();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Unable to setup Firebase connection. See the log for details");
}
dataProvider = new FirebaseDataProvider<>(Run.class, RunDB.getRunsDb());
grid.setDataProvider(dataProvider);
this.sampleRunService = sampleRunService;
addClassNames("table-view", "flex", "flex-col", "h-full");
// Create UI
SplitLayout splitLayout = new SplitLayout();
splitLayout.setSizeFull();
createGridLayout(splitLayout);
createEditorLayout(splitLayout);
add(splitLayout);
grid.removeColumn(grid.getColumnByKey("key"));
grid.removeColumn(grid.getColumnByKey("id"));
grid.setColumnOrder(
grid.getColumnByKey("name"),
grid.getColumnByKey("distance"),
grid.getColumnByKey("time"),
grid.getColumnByKey("date")
);
grid.getColumnByKey("distance").setHeader("Distanz");
grid.getColumnByKey("time").setHeader("Zeit");
grid.getColumnByKey("date")
.setHeader("Datum");
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
grid.setHeightFull();
// when a row is selected or deselected, populate form
grid.asSingleSelect().addValueChangeListener(event -> {
if (event.getValue() != null) {
UI.getCurrent().navigate(String.format(RUN_EDIT_ROUTE_TEMPLATE, event.getValue().getId()));
} else {
clearForm();
UI.getCurrent().navigate(TableView.class);
}
});
// Configure Form
binder = new BeanValidationBinder<>(Run.class);
binder.forField(date)
.withConverter(new LocalDateLongConverter())
.bind(Run::getDate, Run::setDate);
// Bind fields. This where you'd define e.g. validation rules
binder.bindInstanceFields(this);
cancel.addClickListener(e -> {
clearForm();
refreshGrid();
date.setValue(LocalDate.now());
});
delete.addClickListener(e -> {
clearForm();
refreshGrid();
date.setValue(LocalDate.now());
});
save.addClickListener(e -> {
try {
if (this.run == null) {
this.run = new Run();
}
binder.writeBean(this.run);
sampleRunService.update(this.run);
if (run.getKey() == null) {
RunDB.add(run);
} else {
RunDB.update(run.getKey(), run);
}

clearForm();
refreshGrid();
date.setValue(LocalDate.now());
Notification.show("SamplePerson details stored.");
UI.getCurrent().navigate(TableView.class);
} catch (ValidationException validationException) {
Notification.show("An exception happened while trying to store the samplePerson details.");
}
});
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
Optional<Integer> samplePersonId = event.getRouteParameters().getInteger(RUN_ID);
if (samplePersonId.isPresent()) {
Optional<Run> samplePersonFromBackend = sampleRunService.get(samplePersonId.get());
if (samplePersonFromBackend.isPresent()) {
populateForm(samplePersonFromBackend.get());
} else {
Notification.show(
String.format("The requested samplePerson was not found, ID = %d", samplePersonId.get()), 3000,
Notification.Position.BOTTOM_START);
// when a row is selected but the data is no longer available,
// refresh grid
refreshGrid();
event.forwardTo(TableView.class);
}
}
}
private void createEditorLayout(SplitLayout splitLayout) {
Div editorLayoutDiv = new Div();
editorLayoutDiv.setClassName("flex flex-col");
editorLayoutDiv.setWidth("400px");
Div editorDiv = new Div();
editorDiv.setClassName("p-l flex-grow");
editorLayoutDiv.add(editorDiv);
FormLayout formLayout = new FormLayout();
name = new TextField("Name");
distance = new NumberField("Distanz");
time = new NumberField("Zeit");
date = new DatePicker("Datum");
date.setValue(LocalDate.now());
date.setLocale(Locale.GERMANY);
Component[] fields = new Component[]{name, distance, time, date};
for (Component field : fields) {
((HasStyle) field).addClassName("full-width");
}
formLayout.add(fields);
editorDiv.add(formLayout);
createButtonLayout(editorLayoutDiv);
splitLayout.addToSecondary(editorLayoutDiv);
}
private void createButtonLayout(Div editorLayoutDiv) {
HorizontalLayout buttonLayout = new HorizontalLayout();
buttonLayout.setClassName("w-full flex-wrap bg-contrast-5 py-s px-l");
buttonLayout.setSpacing(true);
cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
delete.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
buttonLayout.add(save, cancel, delete);
editorLayoutDiv.add(buttonLayout);
}
private void createGridLayout(SplitLayout splitLayout) {
Div wrapper = new Div();
wrapper.setId("grid-wrapper");
wrapper.setWidthFull();
splitLayout.addToPrimary(wrapper);
wrapper.add(grid);
}
private void refreshGrid() {
grid.select(null);
grid.getDataProvider().refreshAll();
}
private void clearForm() {
populateForm(null);
}
private void populateForm(Run value) {
this.run = value;
binder.readBean(this.run);
}
}

RunDB.java

package com.example.application.data.db;
import com.example.application.data.entity.Run;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseReference;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.logging.Logger;
public class RunDB {
private static final String DATABASE_NAMESPACE = "runs";
public static DatabaseReference getRunsDb() {
return Firebase.getDb().child(DATABASE_NAMESPACE);
}
public static void maybeCreateInitialData(DataSnapshot snapshot) {
if (snapshot.hasChild(DATABASE_NAMESPACE)) {
return;
}
add(new Run("Paul", 34.4, 120, LocalDate.now().toEpochDay()));
add(new Run("Basti", 20.4, 10, LocalDate.now().toEpochDay()));
}
public static void add(Run run) {
getRunsDb().push().setValueAsync(run);
}
protected static Logger getLogger() {
return Logger.getLogger("RunsDB");
}
public static void update(String key, Run run) {
getLogger().info("Set run " + key + " to " + run);
HashMap<String, Object> toUpdate = new HashMap<>();
toUpdate.put(key, run);
getRunsDb().updateChildrenAsync(toUpdate);
}
public static void delete(Run run) {
getLogger().info("Delete user " + run);
HashMap<String, Object> toUpdate = new HashMap<>();
toUpdate.put(run.getKey(), null);
getRunsDb().updateChildrenAsync(toUpdate);
}
}

FirebaseDataProvider.java

package com.example.application.data;

import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.vaadin.flow.data.provider.AbstractDataProvider;
import com.vaadin.flow.data.provider.DataProviderListener;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.function.SerializablePredicate;
import com.vaadin.flow.shared.Registration;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
/**
* A data provider connected to a given child in a Firebase database.
*
* @param <T>
*/
public class FirebaseDataProvider<T extends HasKey>
extends AbstractDataProvider<T, SerializablePredicate<T>>
implements ChildEventListener {
private LinkedHashMap<String, T> data = new LinkedHashMap<>();
private DatabaseReference databaseReference;
private Class<T> type;
AtomicInteger registeredListeners = new AtomicInteger(0);
/**
* Constructs a new Firebase data provider connected to the given database
* reference.
*
* @param type              the entity type to use for items
* @param databaseReference the reference containing the child nodes to include
*/
public FirebaseDataProvider(Class<T> type,
DatabaseReference databaseReference) {
this.databaseReference = databaseReference;
this.type = type;
}
@Override
public String getId(T item) {
return item.getKey();
}
@Override
public Stream<T> fetch(Query<T, SerializablePredicate<T>> query) {
return data.values().stream();
}
@Override
public int size(Query<T, SerializablePredicate<T>> query) {
return data.size();
}
@Override
public boolean isInMemory() {
return false;
}
@Override
public Registration addDataProviderListener(
DataProviderListener<T> listener) {
if (registeredListeners.incrementAndGet() == 1) {
registerFirebaseListener();
}
Registration realRegistration = super.addDataProviderListener(listener);
return () -> {
realRegistration.remove();
if (registeredListeners.decrementAndGet() == 0) {
unregisterFirebaseListener();
}
};
}
private void registerFirebaseListener() {
databaseReference.addChildEventListener(this);
}
private void unregisterFirebaseListener() {
databaseReference.removeEventListener(this);
}
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
T added = snapshot.getValue(type);
String key = snapshot.getKey();
added.setKey(key);
data.put(key, added);
refreshAll();
}
@Override
public void onChildChanged(DataSnapshot snapshot,
String previousChildName) {
T updated = snapshot.getValue(type);
String key = snapshot.getKey();
updated.setKey(key);
data.put(key, updated);
refreshItem(updated);
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
data.remove(snapshot.getKey());
refreshAll();
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
}
@Override
public void onCancelled(DatabaseError error) {
}
}

我感谢你的帮助。非常感谢。Paul

根据query参数的偏移量和限制值,数据提供程序中的fetch方法一次只能加载一页数据。这是一个常见的遗漏,因此它被实现为主动检查getLimit()getOffset()是否运行过,以帮助开发人员意识到缺少了什么。

在添加任何数据之前不会发生这种情况,因为size方法返回0,这反过来意味着甚至没有任何理由调用fetch

最新更新