Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java Data Access Performance in Combination with Vaadin and REST API

Java Data Access Performance in Combination with Vaadin and REST API

Simon Martinelli

January 09, 2022
Tweet

More Decks by Simon Martinelli

Other Decks in Programming

Transcript

  1. 5

  2. 7

  3. 8 In-Memory vs Lazy Callbacks • In-Memory • Easy to

    use if you only have a handful of items • Lazy callbacks • For larger amount of data List<Customer> customers = customerRepository.findAll(); grid.setItems(customers); grid.setItems(query -> customerRepository.findAll( PageRequest.of(query.getPage(), query.getPageSize()) ).stream() );
  4. 9 The Query Object int getOffset(); int getLimit(); int getPage();

    int getPageSize(); List<QuerySortOrder> getSortOrders(); Optional<F> getFilter();
  5. 10 Paging and Sorting • List<QuerySortOrder> getSortOrders() provides the sorted

    columns with sort direction (ascending or descending) • VaadinSpringDataHelpers.toSpringDataSort() converts the Query to a Spring Data Sort object grid.setItems(query -> customerRepository.findAll(PageRequest.of(query.getPage(), query.getPageSize(), toSpringDataSort(query)) ).stream() );
  6. 11 Lazy Data View • Provide an optional CountCallback to

    improve scrollbar behavior • Take care of performance because of the additional count query! grid.setItems( query -> customerRepository.findAll( PageRequest.of(query.getPage(), query.getPageSize(), toSpringDataSort(query))).stream(), query -> (int) customerRepository.count() );
  7. 12 Item Count Estimate • If you can’t implement a

    CountCallback you can set estimates: GridLazyDataView<Employee> dataView = grid.setItems( query -> employeeRepository .findAll(PageRequest.of(query.getOffset(),query.getLimit())) .stream()); dataView.setItemCountEstimate(1000); dataView.setItemCountEstimateIncrease(500);
  8. 13 Filtering • ComboBox is filterable out of the box

    • In Grids with lazy fetching we can use the filter in the FetchCallback filter = new TextField(); filter.setValueChangeMode(ValueChangeMode.LAZY); filter.addValueChangeListener(event -> loadData(event.getValue())); private void loadData(String name) { grid.setItems( query -> customerRepository.findAllCustomersWithRevenue( PageRequest.of(query.getPage(), query.getPageSize(), toSpringDataSort(query)), name).stream() ); }
  9. 14 ConfigurableFilterDataProvider • With ConfigurableFilterDataProvider you can pass a filter

    CallbackDataProvider<CustomerInfo, String> callbackDataProvider = DataProvider.fromFilteringCallbacks( // FetchCallback query -> customerRepository.findAllCustomersWithRevenue( PageRequest.of(query.getPage(), query.getPageSize(), toSpringDataSort(query)), query.getFilter().orElse("")).stream(), // CountCallback query -> customerRepository.countAllByLastnameLikeOrFirstnameLike(query.getFilter().orElse("")) ); // Set the DataProvider ConfigurableFilterDataProvider<CustomerInfo, Void, String> dataProvider = callbackDataProvider.withConfigurableFilter(); grid.setDataProvider(dataProvider); // Use the filter filter.addValueChangeListener(event -> { dataProvider.setFilter(event.getValue()); });
  10. 15 Identity Provider • Vaadin uses equals() to identify an

    object • What if the object doesn't implement equals()? • Define an identifierGetter new CallbackDataProvider<>( query -> employeeRepository.findAll( PageRequest.of(query.getOffset(), query.getLimit())) .stream(), query -> employeeRepository.count(), Employee::getId // Identifier! );
  11. 16 Refreshing • You can refresh all or just one

    element grid.getLazyDataView().refreshAll(); grid.getLazyDataView().refreshItem(employee);
  12. 17 Hierarchical Data Provider new AbstractBackEndHierarchicalDataProvider<>() { @Override public int

    getChildCount(HierarchicalQuery<Employee, Void> query) { if (query.getParent() == null) { return employeeRepository.countBySupervisorIsNull(); } else { return employeeRepository.countBySupervisor(query.getParent()); } } @Override public boolean hasChildren(Employee employee) { return employeeRepository.countBySupervisor(employee) > 0; } @Override protected Stream<Employee> fetchChildrenFromBackEnd(HierarchicalQuery<Employee, Void> query) { if (query.getParent() == null) { return employeeRepository.findAllBySupervisorIsNull().stream(); } else { return employeeRepository.findAllBySupervisor(query.getParent()).stream(); } } };
  13. 18 Data Provider and JPA/Hibernate • JPA Entities can be

    used • BUT be aware of lazy loading and the n+1 select problem! • Consider using projection with DTOs • Java 16 Records are a great fit public record CustomerInfo(Long id, String lastname, String firstname, double revenue) { }
  14. 20 Stop Mapping Stuff in Your Middleware • Modern databases

    provide functions to return XML or JSON data • Often it’s not necessary to make a detour using objects • https://blog.jooq.org/2019/11/13/stop-mapping-stuff-in-your-middleware-use-sqls- xml-or-json-operators-instead/ • https://www.youtube.com/watch?time_continue=973&v=wTPGW1PNy_Y
  15. 21 JSON Query select json_agg(po) as orders from (select p.id,

    p.order_date, (select json_agg(oi) from (select i.id, i.quantity, pr.name as product from order_item as i join product pr on pr.id = i.product_id where i.order_id = p.id ) oi ) as items from purchase_order as p where p.customer_id = ?) po
  16. 22 JSON Result [ { "id": 6232, "order_date": "2021-05-07", "items":

    [ { "id": 32446, "quantity": 7, "product": "Pencil" }, { "id": 32447, "quantity": 7, "product": "Pencil" } } ]
  17. 23 XML Query select xmlelement( name "orders", xmlagg(xmlelement(name "order", xmlattributes(p.id,

    p.order_date), xmlelement(name "items", (select xmlagg(xmlelement(name "item", xmlattributes(i.id, i.quantity, pr.name))) from order_item i join product pr on i.product_id = pr.id where i.order_id = p.id))) ) ) from purchase_order p where p.customer_id = ?
  18. 25 Conclusion • Displaying data with Vaadin is very easy

    but use lazy data providers to reduce memory size and enhance performance • Use projection with DTOs or Records instead of JPA Entities • Consider using SQL