I'm trying to create a Webinterface for my Discord Bot which allows the User to change some Settings of the Bot on their Server.
I store those Settings using Spring JPA. The Problem is that the stored information never shows up in the Crud Component but i'm able to create new information through it.
Storage Class:
@Table(name = "guild_temp_channel_settings")
@Entity
public class GuildTempChannelSettings {
@GeneratedValue
@Id
@Getter
@Setter
private Long id;
@Getter
@Setter
private long guildId;
@Getter
@Setter
private long channelId;
@Getter
@Setter
private String channelName;
}
Repository:
public interface GuildTempChannelSettingsRepository extends JpaRepository<GuildTempChannelSettings, Long> {
List<GuildTempChannelSettings> findByGuildId(long guildId);
}
DataProvider:
@Slf4j
public class GuildTempChannelSettingsDataProvider extends AbstractBackEndDataProvider<GuildTempChannelSettings, CrudFilter> {
private final GuildTempChannelSettingsRepository tempChannelSettingsRepository;
private final long guildId;
public GuildTempChannelSettingsDataProvider(GuildTempChannelSettingsRepository tempChannelSettingsRepository, long guildId) {
this.tempChannelSettingsRepository = tempChannelSettingsRepository;
this.guildId = guildId;
}
@Override
protected Stream<GuildTempChannelSettings> fetchFromBackEnd(Query<GuildTempChannelSettings, CrudFilter> query) {
int offset = query.getOffset();
int limit = query.getLimit();
Stream<GuildTempChannelSettings> stream = tempChannelSettingsRepository.findByGuildId(this.guildId).stream();
log.info("Found {} entries", stream.count());
if (query.getFilter().isPresent()) {
stream = stream
.filter(predicate(query.getFilter().get()))
.sorted(comparator(query.getFilter().get()));
}
return stream.skip(offset).limit(limit);
}
@Override
protected int sizeInBackEnd(Query<GuildTempChannelSettings, CrudFilter> query) {
return tempChannelSettingsRepository.findByGuildId(this.guildId).size();
}
private static Predicate<GuildTempChannelSettings> predicate(CrudFilter filter) {
// For RDBMS just generate a WHERE clause
return filter.getConstraints().entrySet().stream()
.map(constraint -> (Predicate<GuildTempChannelSettings>) person -> {
try {
Object value = valueOf(constraint.getKey(), person);
return value != null && value.toString().toLowerCase()
.contains(constraint.getValue().toLowerCase());
} catch (Exception e) {
e.printStackTrace();
return false;
}
})
.reduce(Predicate::and)
.orElse(e -> true);
}
private static Comparator<GuildTempChannelSettings> comparator(CrudFilter filter) {
return filter.getSortOrders().entrySet().stream()
.map(sortClause -> {
try {
Comparator<GuildTempChannelSettings> comparator = Comparator.comparing(settings ->
(Comparable) valueOf(sortClause.getKey(), settings)
);
if (sortClause.getValue() == SortDirection.DESCENDING) {
comparator = comparator.reversed();
}
return comparator;
} catch (Exception ex) {
return (Comparator<GuildTempChannelSettings>) (o1, o2) -> 0;
}
})
.reduce(Comparator::thenComparing)
.orElse((o1, o2) -> 0);
}
private static Object valueOf(String fieldName, GuildTempChannelSettings person) {
try {
Field field = GuildTempChannelSettings.class.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(person);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Layout the Crud is used in:
private VerticalLayout createTemporaryChannelSettings() {
VerticalLayout layout = new VerticalLayout();
layout.addClassNames("box l radius", "contrast-5pct");
Span title = new Span("Coming Soon");
title.addClassNames("header-text", "font-size-xxl");
layout.add(title);
Hr titleLine = new Hr();
layout.add(titleLine);
Crud<GuildTempChannelSettings> crud = new Crud<>(GuildTempChannelSettings.class, createGrid(), createCrudEditor());
GuildTempChannelSettingsDataProvider dataProvider = new GuildTempChannelSettingsDataProvider(this.guildTempChannelSettingsRepository, this.guild.getIdLong());
crud.setDataProvider(dataProvider);
crud.addDeleteListener(deleteEvent -> {
//TODO delete channel
this.guildTempChannelSettingsRepository.delete(deleteEvent.getItem());
});
crud.addSaveListener(saveEvent -> {
//TODO create channel
this.guildTempChannelSettingsRepository.save(saveEvent.getItem());
});
layout.add(crud);
Html total = new Html("<span>Total: <b>" + this.guildTempChannelSettingsRepository.findByGuildId(this.guild.getIdLong()).size() + "</b> employees</span>");
Button button = new Button("New", VaadinIcon.PLUS.create());
button.addClickListener(event -> crud.edit(new GuildTempChannelSettings(), Crud.EditMode.NEW_ITEM));
button.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
HorizontalLayout toolbar = new HorizontalLayout(total, button);
toolbar.setAlignItems(FlexComponent.Alignment.CENTER);
toolbar.setFlexGrow(1, toolbar);
toolbar.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
toolbar.setSpacing(false);
crud.setToolbar(toolbar);
return layout;
}
I've tried implementing this using the Vaadin Crud component docs but it never shows some data inside if the component. The fetchFromBackEnd
in the DataProvider is never invoked (not sure this is important).
Am i missing something to load the information?
Edit:
I was able to fix this by adding crud.addNewListener(newEvent -> newEvent.getItem().setGuildId(this.guild.getIdLong()));
to the Crud component. Thanks to Simon Martinelli who gave me the hint.
Sadly i'm running now into a stream has already been operated upon or closed
Exception when fetchFromBackEnd
has been called.
I got no idea what's going on there :S
The first issue could be solved by adding crud.addNewListener(newEvent -> newEvent.getItem().setGuildId(this.guild.getIdLong()));
to the layout.
Thanks to Simon Martinelli :)
The other issue could be solved by using a List instead of a Stream in the DataProvider fetchFromBackEnd
method