Vaadin + Spring Boot + Spring Data JPA CRUD example

Hello everyone, today we will learn how to develop a simple CRUD web application using Vaadin, Spring Boot, Spring Data JPA, and H2 Database.

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.

The GitHub repository link is provided at the end of this tutorial. You can download the source code.



Technologies Used:

  • Spring Boot 2.7.0
  • JDK 17
  • Vaadin 14.7.0
  • Maven 3+
  • npm package manager
  • H2 Database


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


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.7.0</version>
<relativePath/>
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>springboot-vaadin-jpa-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-vaadin-jpa-crud</name>
<description>Demo project for Spring Boot + vaadin</description>
<properties>
<java.version>17</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>

<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>


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


Create User Entity 

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

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

@Entity
@Table(name = "UserTable")
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 Java Files

Index.java

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

import org.springframework.util.StringUtils;
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.button.ButtonVariant;
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;

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

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

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
("Add User",VaadinIcon.PLUS.create());
addNewBtn.addThemeVariants
(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_CONTRAST);

// 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 Driver

package com.knf.dev.demo.springvaadincrud;

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

@SpringBootApplication
public class Application {

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


Download the complete source code - click here                                     

Local Setup and Run the application


Step 1: Download or clone the source code from GitHub to a local machine - Click here


Step 2: mvn clean install


Step 3: Run the Spring Boot application - mvn spring-boot:run


From the browser call the endpoint http://localhost:8080/

Popular posts from this blog

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

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete