Slide 1

Slide 1 text

You may not need JavaScript Single Page Applications for Java Developers Simon Martinelli @simas_ch martinelli.ch

Slide 2

Slide 2 text

About me

Slide 3

Slide 3 text

ERP UI Modernization A Real-World Project

Slide 4

Slide 4 text

Existing System • Existing ERP System based on Oracle Forms • Customizable by the customer → Goal: Build a modern web frontend ~800 Forms ~2000 Tables und Views ~5000 Procedures und Functions ~100 User Defined Types

Slide 5

Slide 5 text

Goal

Slide 6

Slide 6 text

UI Editor

Slide 7

Slide 7 text

Runtime Generated UI UI Data Generate UI User accesses module Based on user rights and data UI ready

Slide 8

Slide 8 text

UI Frameworks

Slide 9

Slide 9 text

UI Models • Server-Side Rendering • JSF • Spring MVC with Thymeleaf • Single Page Application • React, Angular, Vue etc. • Vaadin https://msdn.microsoft.com/en-us/magazine/dn463786.aspx

Slide 10

Slide 10 text

State • Session Scope • Single Page Application • Vaadin

Slide 11

Slide 11 text

https://vaadin.com

Slide 12

Slide 12 text

History HTML AJAX GWT Web Components .1 3 4 5 6 7 8 10 TypeScript Lit .2 LTS 14 15 Vaadin Components & Flow 23 … Hilla

Slide 13

Slide 13 text

Why Vaadin? • Type-safe Java API • A rich component model • Grids with paging, sorting, and filtering • Forms with validation and conversion • Bi-directional Data Binding • No REST API • Direct access from UI code to services or repositories → A great fit for data-centric business applications

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Example Code Vaadin Flow @Route public class HelloView extends VerticalLayout { public HelloView() { TextField textField = new TextField("Your Name"); Label label = new Label(); Button button = new Button("Greet", e -> label.setText("Hello, " + textField.getValue())); add(textField, label, button); } }

Slide 16

Slide 16 text

Routing @Route(value = "S450", layout = MainLayout.class) public class S450 implements HasUrlParameter { @Override public void setParameter(BeforeEvent event, @OptionalParameter String parameter) { Location location = event.getLocation(); QueryParameters queryParameters = location.getQueryParameters(); Map> parameterMap = queryParameters.getParameters(); ... } }

Slide 17

Slide 17 text

Grid • Paging with lazy loading • Sortable, filterable and editable Grid grid = new Grid<>(CustomerRecord.class); grid.setColumns("id", "firstName", "lastName", "email"); Grid.Column editColumn = grid.addColumn( new ComponentRenderer<>(customer -> new RouterLink("Edit", CustomerView.class, customer.getId()))); editColumn.setFrozen(true); grid.setColumnReorderingAllowed(true);

Slide 18

Slide 18 text

Data Provider // In-Memory grid.setItems(List.of("Barcelona, "Berne"); // Lazy grid.setItems( query -> repository.findAll(createFilter(query), createOrderBy(query), query.getOffset(), query.getLimit()), query -> repository.count(createFilter(query)) );

Slide 19

Slide 19 text

Forms • mit und binder.forField(id) .withNullRepresentation("") .withConverter(new StringToIntegerConverter(0, "Integers only")) .withValidator((value, context) -> value > 0 ? ValidationResult.ok() : ValidationResult.error("Id must be greater than 0")) .bind(EmployeeRecord::getId, EmployeeRecord::setId);

Slide 20

Slide 20 text

Push • Activated with @Push • Uses Atmosphere button.addClickListener(event -> taskExecutor.execute(() -> { // do something that takes a while button.getUI().ifPresent(ui -> ui.access(() -> Notification.show("Done!"))); }));

Slide 21

Slide 21 text

Testbench • Uses Selenium and WebDrivers @Test public void say_hello() { $(TextFieldElement.class).id("name").setValue("World"); $(ButtonElement.class).id("say-hello").click(); LabelElement text = $(LabelElement.class).id("text"); assertEquals("Hello World", text.getText()); }

Slide 22

Slide 22 text

Karibu Testing • Unit Testing library for Vaadin • Open Source https://github.com/mvysny/karibu-testing @Test void say_hello() { _get(TextField.class, spec -> spec.withId("name")).setValue("World"); _get(Button.class, spec -> spec.withId("say-hello")).click(); Label label = _get(Label.class, spec -> spec.withId("text")); assertThat(label.getText()).isEqualTo("Hello World"); }

Slide 23

Slide 23 text

Spa, PWA et cetera • A Vaadin application is a SPA • PWA Support • Web App Manifest • Information about an application, e.g., name, theme, icon and description. These details are required to create an installable version of the web application • Service Worker • Runs separately from the browser thread • Intercepts network requests • Cached and retrieves resources from the cache • Delivers push notifications

Slide 24

Slide 24 text

https://jooq.org

Slide 25

Slide 25 text

jOOQ • Type-safe SQL with Java • Two main componentes • Generator • DSL

Slide 26

Slide 26 text

jOOQ Generator org.jooq jooq-codegen-maven org.jooq.meta.extensions.ddl.DDLDatabase scripts src/main/resources/db/migration/*.sql ch.martinelli.vaadin.demo.db target/generated-sources/jooq

Slide 27

Slide 27 text

Metamodel public class Employee extends TableImpl { public final TableField ID = createField(DSL.name("ID"), SQLDataType.INTEGER.nullable(false) .defaultValue(DSL.field("NEXT VALUE FOR \"PUBLIC\".\"SECURITY_GROUP_SEQ\"", SQLDataType.INTEGER)), this, ""); public final TableField NAME = createField("NAME", SQLDataType.VARCHAR(255).nullable(false), this, ""); public final TableField DEPARTMENT_ID = createField("DEPARTMENT_ID", SQLDataType.INTEGER.nullable(false), this, ""); @Override public UniqueKey getPrimaryKey() { return Keys.CONSTRAINT_7; } ... }

Slide 28

Slide 28 text

jOOQ DSL Result records = dslContext .selectFrom(EMPLOYEE) .fetch(); dslContext .insertInto(DEPARTMENT) .columns(DEPARTMENT.NAME) .values("IT") .execute(); Result> records = dslContext .select(EMPLOYEE.NAME) .from(EMPLOYEE) .join(DEPARTMENT).on(DEPARTMENT.ID.eq(EMPLOYEE.DEPARTMENT_ID)) .where(DEPARTMENT.NAME.eq("IT")) .fetch();

Slide 29

Slide 29 text

CRUD with jOOQ • jOOQ generates for each table with a unique key an implementation of UpdateableRecord // Store (insert or update) a record to the database. int store() throws DataAccessException; // Delete a record from the database int delete() throws DataAccessException; // Refresh a record from the database. void refresh() throws DataAccessException;

Slide 30

Slide 30 text

Let's See Some Code https://start.vaadin.com

Slide 31

Slide 31 text

Conclusion • Creating the entire UI with Java makes the life (for Java developers) easier • jOOQ enables type-safe SQL in Java • Vaadin and jOOQ are a great combination when developing data centric business apps

Slide 32

Slide 32 text

Thank you! • Example code https://github.com/simasch/spring-io-2022-vaadin • Web https://martinelli.ch • E-Mail [email protected] • Twitter simas_ch • LinkedIn https://linkedin.com/in/simonmartnelli