Slide 1

Slide 1 text

Java Data Access Performance in Combination with Vaadin and REST API Simon Martinelli, 72 Services LLC

Slide 2

Slide 2 text

2 About me @simas_ch

Slide 3

Slide 3 text

3 Vaadin Data Access https://github.com/72services/ vaadin-database-performance

Slide 4

Slide 4 text

4 Crud (Screenshot tosca web)

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

6 Display Customers Revenue

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

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 customers = customerRepository.findAll(); grid.setItems(customers); grid.setItems(query -> customerRepository.findAll( PageRequest.of(query.getPage(), query.getPageSize()) ).stream() );

Slide 9

Slide 9 text

9 The Query Object int getOffset(); int getLimit(); int getPage(); int getPageSize(); List getSortOrders(); Optional getFilter();

Slide 10

Slide 10 text

10 Paging and Sorting • List 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() );

Slide 11

Slide 11 text

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() );

Slide 12

Slide 12 text

12 Item Count Estimate • If you can’t implement a CountCallback you can set estimates: GridLazyDataView dataView = grid.setItems( query -> employeeRepository .findAll(PageRequest.of(query.getOffset(),query.getLimit())) .stream()); dataView.setItemCountEstimate(1000); dataView.setItemCountEstimateIncrease(500);

Slide 13

Slide 13 text

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() ); }

Slide 14

Slide 14 text

14 ConfigurableFilterDataProvider • With ConfigurableFilterDataProvider you can pass a filter CallbackDataProvider 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 dataProvider = callbackDataProvider.withConfigurableFilter(); grid.setDataProvider(dataProvider); // Use the filter filter.addValueChangeListener(event -> { dataProvider.setFilter(event.getValue()); });

Slide 15

Slide 15 text

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! );

Slide 16

Slide 16 text

16 Refreshing • You can refresh all or just one element grid.getLazyDataView().refreshAll(); grid.getLazyDataView().refreshItem(employee);

Slide 17

Slide 17 text

17 Hierarchical Data Provider new AbstractBackEndHierarchicalDataProvider<>() { @Override public int getChildCount(HierarchicalQuery 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 fetchChildrenFromBackEnd(HierarchicalQuery query) { if (query.getParent() == null) { return employeeRepository.findAllBySupervisorIsNull().stream(); } else { return employeeRepository.findAllBySupervisor(query.getParent()).stream(); } } };

Slide 18

Slide 18 text

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) { }

Slide 19

Slide 19 text

19 Data Access in REST APIs

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

22 JSON Result [ { "id": 6232, "order_date": "2021-05-07", "items": [ { "id": 32446, "quantity": 7, "product": "Pencil" }, { "id": 32447, "quantity": 7, "product": "Pencil" } } ]

Slide 23

Slide 23 text

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 = ?

Slide 24

Slide 24 text

24 XML Result

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

26 72 Services GmbH Tulpenweg 1 2575 Täuffelen, Switzerland +41 32 544 88 88 info@72.services https://72.services