Быстрая разработка Web приложения на Vaadin и Spring Boot

от автора

КПДВ

Целью данной статьи является систематизация процесса разработки веб приложения на Vaadin 14 с использованием Spring Boot.

Перед прочтением данной статьи, рекомендую прочитать следующий материал:

Впечатления

Vaadin 14 — довольно удобное средство для проектирования веб приложений, до знакомства с ним разрабатывал графические интерфейсы только на JavaFX, Android, и даже J2ME, и избегал при этом frontend разработки (базовые знания HTML, CSS, JS имеются) потому что считал что это не мое.

Disclaimer

Те кто не работал еще с Spring Boot рекомендую пропустить быстрый старт с помощью Spring Initializr, вернуться к рекомендуемому материалу, и попробовать настроить все самостоятельно, наткнувшись на множество различных проблем, иначе в дальнейшем возникнут пробелы в понимании различных вещей.

Быстрый старт

Создадим проект для нашего web-приложения с помощью Spring Initializr, необходимые зависимости для нашего маленького web-приложения:

  • Spring Data JPA (для работы с базой данных)
  • Vaadin (для разработки веб-приложения)
  • Lombok (для уменьшения boiler-plate кода)
  • MySQL Driver (я использую mariadb, в spring initializr’e его нет)

Настройка application.properties и базы данных

Проект созданный на Spring Initializr практически готов к запуску, нам остается только настроить application.properties указав путь к базе данных, логин и пароль

spring.datasource.url = jdbc:mariadb://127.0.0.1:3306/test spring.datasource.username=user spring.datasource.password=password spring.jpa.hibernate.ddl-auto=update

Внимание: ddl-auto

Не используйте ddl-auto со значением update на живой базе или при разработке проекта, так как он автоматически обновляет схему базы данных.
Существующие параметры для ddl-auto:
create — создаст таблицу в базе данных, предварительно удалив старую версию таблицы (потеря данных в случае изменения схемы)
validate — проверяет таблицу в базе данных, если она не соответствует сущности то hibernate выбросит исключение
update — проверяет таблицу, и автоматически ее обновляет без удаления несуществующих полей из сущности
create-drop — проверяет таблицу, создает или обновляет ее, а потом удаляет, предназначен для модульного тестирования

С установленным значением ddl-auto:update — hibernate автоматически создает таблицу в базе данных на основе нашей сущности, т.к. мы делаем простую адресную книгу то создадим класс контакта.

Contact.class

@Entity(name = "Contacts") @Getter @Setter public class Contact {     @Id     @GeneratedValue(strategy = GenerationType.AUTO)     private int id;     private String firstName;     private String secondName;     private String fatherName;     private String numberPhone;     private String email; }

Создадим интерфейс для работы с базой данных, и добавим метод возвращающий List т.к. Spring Data JPA по умолчанию возвращает Iterable вместо List.

ContactRepository.class

public interface ContactRepository extends CrudRepository<Contact, Integer> {     List<Contact> findAll();     }

Разработка интерфейса на Vaadin включает в себя добавление компонентов форм ввода, визуализации и взаимодействия в объекты макетов для необходимого позиционирования элементов. Список всех компонентов можно посмотреть на официальном сайте framework’а

Основной страницей нашего приложения будет ContactList. Все объекты созданных страниц будем наследовать от AppLayout — это типовой макет веб приложения состоящий из:

  • Navbar (шапка)
  • Drawer (боковая панель)
  • Content (содержимое)
    При этом в Navbar и Drawer добавляются компоненты, а в Content устанавливается компонент в качестве содержимого, к примеру VerticalLayout в котором будут размещаться пользовательские элементы в вертикальном расположении.

Страницей редактирования и создания контактов будет ManageContact, и реализуем в нем интерфейс HasUrlParameter для передачи id контакта, при включении данного интерфейса обязательно наличие переданного параметра странице.
Для того чтобы страницу привязать к определенному URL используется аннотация Route:

@Route("contacts") public class ContactList extends AppLayout {}  @Route("manageContact") public class ManageContact extends AppLayout implements HasUrlParameter<Integer> {}

Создание списка контактов

В конструкторе объекта ContactList, указываем используемые компоненты предварительно сделав их полями объекта. Так как данные будут браться из базы данных, то необходимо подключить репозиторий в поле объекта.

@Route("contacts") public class ContactList extends AppLayout {     VerticalLayout layout;     Grid<Contact> grid;     RouterLink linkCreate;      @Autowired     ContactRepository contactRepository;      public ContactList(){         layout = new VerticalLayout();         grid = new Grid<>();         linkCreate = new RouterLink("Создать контакт",ManageContact.class,0);         layout.add(linkCreate);         layout.add(grid);         addToNavbar(new H3("Список контактов"));         setContent(layout);     } }

Об Autowired

Не пытайтесь получить доступ к contactRepository из конструктора объекта это непременно вызовет NullPointerException, получайте доступ из методов с аннотацией PostConstruct, или методов уже созданного объекта.

В класс ContactList добавлен компонент VerticalLayout для вертикального расположения элементов в содержимом, в него добавим RouterLink (для перехода на страницу создания контакта) и Grid для отображения таблицы. Grid типизирован объектом Contact для того чтобы мы могли загрузить из списка данные и они автоматом подтянулись при вызове метода setItems();

Grid<Contact> grid; public ContactList(){         grid = new Grid<>(); // Создаст пустую таблицу без колонок         grid = new Grid<>(Contact.class); // Автоматически заполнит таблицу полями из объекта, дав им названия соответствующие наименованию поля     }

Поведение автоматического создания колонок нам не интересно, т.к. в рамках статьи стоит показать добавление колонок, и отображение кнопок для удаления или редактирования контактов.

Для заполнения таблицы получим данные из contactRepository, для этого создадим метод с аннотацией PostConstruct

Заполнение таблицы с помощью fillGrid()

    @PostConstruct     public void fillGrid(){         List<Contact> contacts = contactRepository.findAll();         if (!contacts.isEmpty()){             //Выведем столбцы в нужном порядке             grid.addColumn(Contact::getFirstName).setHeader("Имя");             grid.addColumn(Contact::getSecondName).setHeader("Фамилия");             grid.addColumn(Contact::getFatherName).setHeader("Отчество");             grid.addColumn(Contact::getNumberPhone).setHeader("Номер");             grid.addColumn(Contact::getEmail).setHeader("E-mail");             //Добавим кнопку удаления и редактирования             grid.addColumn(new NativeButtonRenderer<Contact>("Редактировать", contact -> {                 UI.getCurrent().navigate(ManageContact.class,contact.getId());             }));             grid.addColumn(new NativeButtonRenderer<Contact>("Удалить", contact -> {                 Dialog dialog = new Dialog();                 Button confirm = new Button("Удалить");                 Button cancel = new Button("Отмена");                 dialog.add("Вы уверены что хотите удалить контакт?");                 dialog.add(confirm);                 dialog.add(cancel);                 confirm.addClickListener(clickEvent -> {                    contactRepository.delete(contact);                    dialog.close();                    Notification notification = new Notification("Контакт удален",1000);                    notification.setPosition(Notification.Position.MIDDLE);                    notification.open();                    grid.setItems(contactRepository.findAll());                 });                 cancel.addClickListener(clickEvent -> {                     dialog.close();                 });                 dialog.open();             }));             grid.setItems(contacts);         }     }

Для добавления колонок с кнопками редактирования и удаления используется NativeButtonRenderer, в аргументы конструктора передаем название кнопки, и обработчик нажатия на кнопку.

grid.addColumn(new NativeButtonRenderer<Contact>("Редактировать", contact -> {        //DO SOMETHING })); grid.addColumn(new NativeButtonRenderer<Contact>("Редактирование", new ClickableRenderer.ItemClickListener<Contact>() {           @Override           public void onItemClicked(Contact contact) {              //DO SOMETHING           }}));

Результат

Список контактов

Создание страницы редактирования контактов

Страница редактирования контактов принимает параметр в виде id контакта, поэтому нам необходимо имплементировать метод setParameter():

    @Override     public void setParameter(BeforeEvent beforeEvent, Integer contactId) {         id = contactId;         if (!id.equals(0)){             addToNavbar(new H3("Редактирование контакта"));         }         else {             addToNavbar(new H3("Создание контакта"));         }         fillForm(); //Заполнение формы     }

Добавление компонентов аналогично ContactList, только в данном случае мы не используем VerticalLayout, а используем FormLayout специальную разметку для отображения подобных форм. Заполняем форму данными уже не с помощью метода с аннотацией PostConstruct, а после получения номера контакта из URL, потому что цепочка: Конструктор объекта -> @PostConstruct -> Override

ManageContact.class

@Route("manageContact") public class ManageContact extends AppLayout implements HasUrlParameter<Integer> {      Integer id;     FormLayout contactForm;     TextField firstName;     TextField secondName;     TextField fatherName;     TextField numberPhone;     TextField email;     Button saveContact;      @Autowired     ContactRepository contactRepository;      public ManageContact(){         //Создаем объекты для формы         contactForm = new FormLayout();         firstName = new TextField("Имя");         secondName = new TextField("Фамилия");         fatherName = new TextField("Отчество");         numberPhone = new TextField("Номер телефона");         email = new TextField("Электронная почта");         saveContact = new Button("Сохранить");         //Добавим все элементы на форму         contactForm.add(firstName, secondName,fatherName,numberPhone,email,saveContact);         setContent(contactForm);     }      @Override     public void setParameter(BeforeEvent beforeEvent, Integer contactId) {         id = contactId;         if (!id.equals(0)){             addToNavbar(new H3("Редактирование контакта"));         }         else {             addToNavbar(new H3("Создание контакта"));         }         fillForm();     }      public void fillForm(){         if (!id.equals(0)){             Optional<Contact> contact = contactRepository.findById(id);             contact.ifPresent(x -> {                 firstName.setValue(x.getFirstName());                 secondName.setValue(x.getSecondName());                 fatherName.setValue(x.getFatherName());                 numberPhone.setValue(x.getNumberPhone());                 email.setValue(x.getEmail());             });         }         saveContact.addClickListener(clickEvent->{             //Создадим объект контакта получив значения с формы             Contact contact = new Contact();             if (!id.equals(0)){                 contact.setId(id);             }             contact.setFirstName(firstName.getValue());             contact.setSecondName(secondName.getValue());             contact.setFatherName(fatherName.getValue());             contact.setEmail(email.getValue());             contact.setNumberPhone(numberPhone.getValue());             contactRepository.save(contact);              Notification notification = new Notification(id.equals(0)? "Контакт успешно создан":"Контакт был изменен",1000);             notification.setPosition(Notification.Position.MIDDLE);             notification.addDetachListener(detachEvent -> {                 UI.getCurrent().navigate(ContactList.class);             });             contactForm.setEnabled(false);             notification.open();         });     } }

Результат

Редактирование контакта

Итоги: Vaadin 14 довольно удобный framework для создания простых web-приложений, с помощью него можно быстро сделать приложение имея в багаже знаний только Java, и оно будет работать. Но к большому сожалению весь интерфейс создается на стороне сервера и ресурсов необходимо гораздо больше нежели если использовать HTML5+JS. Данный framework больше подходит для небольших проектов которые нужно быстро сделать не изучая front-end технологии.

В данной статье было показано как можно быстро и легко создать web-приложение не проектируя предварительно базу данных, избежать длинных xml конфигураций, и то как быстро можно разработать web-интерфейс. По большей части Spring Boot и Spring Data JPA облегчает жизнь разработчика и упрощает разработку. Статья не откроет ничего нового уже состоявшимся разработчикам, но поможет новичку начать осваивать Spring framework.

Репозиторий с проектом

В статье возможны грамматические и пунктационные ошибки, при обнаружении прошу присылать в личку


ссылка на оригинал статьи https://habr.com/ru/post/480852/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *