Vaadin + Spring Boot + JPA CRUD UI example

Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using Vaadin, Spring Boot, and JPA.
Vaadin is the only framework that allows you to write UI 100% in Java without getting bogged down in JS, HTML, and CSS. If you prefer, you can also create layouts in HTML or with a visual designer. Vaadin apps run on the server and handle all communication automatically and securely.
GitHub repository link is provided at the end of this tutorial. You can download the source code.

Following technologies stack being used:

  • Spring Boot 2.5.5
  • JDK 11
  • Vaadin 14.7.0
  • Maven 3
  • npm package manager
  • H2DB

After completing this tutorial what we will build?

We will build a full-stack web application that is a basic User Management Application with CRUD features:
    • Create User
    • List User
    • Update User
    • Delete User
    • View User

Following is the User Interface of our application -


Let's begin building the application,

Project Structure:


Dependency Management - Maven - pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>springvaadincrud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springvaadincrud</name>
<description>Demo project for Spring Boot + vaadin</description>
<properties>
<java.version>11</java.version>
<vaadin.version>14.7.0</vaadin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- tag::starter[] -->
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<!-- end::starter[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<!-- tag::bom[] -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- end::bom[] -->

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Create an Entity Class

package com.knf.dev.demo.springvaadincrud.backend.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
private String email;

protected User() {
}

public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}

Create User Repository

package com.knf.dev.demo.springvaadincrud.backend.repository;

import com.knf.dev.demo.springvaadincrud.backend.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {

List<User> findByEmailStartsWithIgnoreCase(String email);
}

Vaadin UI Classes

Index.java

package com.knf.dev.demo.springvaadincrud.frontend.view;

import com.knf.dev.demo.springvaadincrud.backend.model.User;
import com.knf.dev.demo.springvaadincrud.backend.repository.UserRepository;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
import org.springframework.util.StringUtils;

@Route(value="/")
public class Index extends VerticalLayout {

final Grid<User> grid;
final TextField filter;
private final UserRepository repo;
private final UserEditor editor;
private final Button addNewBtn;

public Index(UserRepository repo, UserEditor editor) {
this.repo = repo;
this.editor = editor;
this.grid = new Grid<>(User.class);
this.filter = new TextField();
this.addNewBtn = new Button
("New User", VaadinIcon.PLUS.create());

// build layout
HorizontalLayout actions = new
HorizontalLayout(filter, addNewBtn);
add(actions, grid, editor);
grid.setHeight("300px");
grid.setColumns("id", "firstName", "lastName", "email");
grid.getColumnByKey("id").setWidth("60px").
setFlexGrow(0);
filter.setPlaceholder("Filter by email");
// Hook logic to components

// Replace listing with filtered content when user changes filter
filter.setValueChangeMode(ValueChangeMode.EAGER);
filter.addValueChangeListener(e -> listUsers(e.getValue()));

// Connect selected User to editor or hide if none is selected
grid.asSingleSelect().addValueChangeListener(e -> {
editor.editUser(e.getValue());
});

// Instantiate and edit new User the new button is clicked
addNewBtn.addClickListener(e -> editor.editUser
(new User("", "", "")));

// Listen changes made by the editor,
// refresh data from backend
editor.setChangeHandler(() -> {
editor.setVisible(false);
listUsers(filter.getValue());
});

// Initialize listing
listUsers(null);
}

void listUsers(String filterText) {
if (StringUtils.isEmpty(filterText)) {
grid.setItems(repo.findAll());
} else {
grid.setItems(repo.
findByEmailStartsWithIgnoreCase(filterText));
}
}}

UserEditor.java

package com.knf.dev.demo.springvaadincrud.frontend.view;

import com.knf.dev.demo.springvaadincrud.backend.model.User;
import com.knf.dev.demo.springvaadincrud.backend.repository.UserRepository;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.KeyNotifier;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.annotation.UIScope;
import org.springframework.beans.factory.annotation.Autowired;


@SpringComponent
@UIScope
public class UserEditor extends VerticalLayout implements KeyNotifier {

private final UserRepository repository;
/* Fields to edit properties in User entity */
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
TextField email = new TextField("Email");
/* Action buttons */
Button save = new Button("Save", VaadinIcon.CHECK.create());
Button cancel = new Button("Cancel");
Button delete = new Button("Delete", VaadinIcon.TRASH.create());
HorizontalLayout actions = new HorizontalLayout(save, cancel, delete);
Binder<User> binder = new Binder<>(User.class);
private User user;
private ChangeHandler changeHandler;

@Autowired
public UserEditor(UserRepository repository) {
this.repository = repository;
add(firstName, lastName, email, actions);
// bind using naming convention
binder.bindInstanceFields(this);
// Configure and style components
setSpacing(true);
save.getElement().getThemeList().add("primary");
delete.getElement().getThemeList().add("error");
addKeyPressListener(Key.ENTER, e -> save());
// wire action buttons to save, delete and reset
save.addClickListener(e -> save());
delete.addClickListener(e -> delete());
cancel.addClickListener(e -> editUser(user));
setVisible(false);
}

void delete() {
repository.delete(user);
changeHandler.onChange();
}

void save() {
repository.save(user);
changeHandler.onChange();
}

public final void editUser(User usr) {
if (usr == null) {
setVisible(false);
return;
}
final boolean persisted = usr.getId() != null;
if (persisted) {
// Find fresh entity for editing
user = repository.findById(usr.getId()).get();
} else {
user = usr;
}
cancel.setVisible(persisted);
// Bind user properties to similarly named fields
// Could also use annotation or "manual binding" or programmatically
// moving values from fields to entities before saving
binder.setBean(user);

setVisible(true);

// Focus first name initially
firstName.focus();
}

public void setChangeHandler(ChangeHandler h) {
// ChangeHandler is notified when either save or delete
// is clicked
changeHandler = h;
}

public interface ChangeHandler {
void onChange();
}

}

Spring Boot Main Class(SpringvaadincrudApplication.java)

package com.knf.dev.demo.springvaadincrud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringvaadincrudApplication {

public static void main(String[] args) {
SpringApplication.run(SpringvaadincrudApplication.class, args);
}

}

Run the application

mvn spring-boot:run

Access the URL: http://localhost:8080/

Popular posts from this blog

Spring boot video streaming example-HTML5

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Spring Boot + Mockito simple application with 100% code coverage

Spring Boot + OpenCSV Export Data to CSV Example

Custom Exception Handling in Quarkus REST API

DataTable-Pagination example with Spring boot, jQuery and ajax

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Node JS mini projects with source code - free download

Spring boot web project free download:User Registration System

Java - Blowfish Encryption and decryption Example