В этом руководстве рассказывается, как подключить ваше веб-приложение к базе данных MySQL, чтобы вы могли создавать, считывать, обновлять и удалять (CRUD) информацию в базе данных через веб-приложение Vaadin. Идея состоит в том, чтобы показать вам, как подключить шаблонный проект, загруженный с https://start.vaadin.com, к базе данных, чтобы вы могли скопировать его для своих собственных нужд. Речь идет не о передовой практике (для Vaadin, Spring или MySQL), а о том, чтобы помочь вам быстро начать работу.
Вы можете найти код, используемый в этом руководстве, из GitHub .
В первой части мы рассмотрели, как начать работу с Vaadin, как открыть загруженный проект в вашей любимой IDE и многое другое.
7. Сохранение данных сотрудника
Далее мы создадим код для вставки и обновления сотрудников. В этом примере они оба работают с одной и той же кнопки Сохранить. Чтобы узнать, является ли пресс обновлением или вставкой, мы проверим, есть ли указанный адрес электронной почты уже в базе данных, которую мы собираемся обновить. Если адрес электронной почты новый, то это вставка.
ПРИМЕЧАНИЕ . Вы можете подумать, должна ли быть кнопка сохранения и вставки, или адрес электронной почты является правильным ключом, который следует использовать для определения нового сотрудника? Возможно, у вас уже есть лучшее решение, и это очень хорошая новость для вас. Вы можете исправить это по своему вкусу позже.
Вы также можете прочитать: Mule 4: Database Connector (часть 1)
Мы начнем с создания метода для сохранения сотрудника в базе данных. Добавьте saveEmployee
метод в класс EmployeeService . Он должен принимать сотрудника в качестве аргумента и возвращает значение int (количество сотрудников, обновленных / вставленных в базу данных).
Джава
xxxxxxxxxx
1
public int saveEmployee(Employee employee) {
2
List employees = this.findByEmail(employee.getEmail());
3
if ( employees.size() > 0 ) {
4
return updateEmployee(employee);
5
} else {
6
return insertEmployee(employee);
7
}
8
}
Вы можете заметить, что метод saveEmployee
использует новые вызываемые методы findByEmail
, updateEmployee
и insertEmployee
. Вам необходимо создать эти методы в классе EmployeeService . Этот findByEmail
метод используется для проверки того, что это электронное письмо уже используется в базе данных. Возвращает список сотрудников с этим адресом электронной почты. Это всегда должно быть 0 или 1, но если вы уже ввели один и тот же адрес электронной почты в базу данных много раз, его может быть больше 1. В этом случае все строки с этим электронным письмом будут обновляться одновременно время. Если адрес электронной почты уже есть в базе данных (размер списка больше 0), мы вызовем updateEmployee
метод. В противном случае мы добавим нового сотрудника, вызвав insertEmployee
метод.
Джава
xxxxxxxxxx
1
public List findByEmail(String email) {
2
try {
3
return jdbcTemplate.query("SELECT firstname, lastname, email, title FROM
4
employees WHERE email = ?", new Object[]{email}, (rs, rowNum) -> new
5
Employee(rs.getString("firstname"), rs.getString("lastname"),
6
rs.getString("email"), rs.getString("title")));
7
} catch (Exception e) {
8
return new ArrayList();
9
}
10
}
Джава
xxxxxxxxxx
1
private int insertEmployee(Employee employee) {
2
try {
3
return jdbcTemplate.update("INSERT INTO employees VALUES (?, ?, ?, ?, ?)",
4
employee.getFirstname(), employee.getLastname(), employee.getTitle(),
5
employee.getEmail(), "");
6
} catch (Exception e) {
7
return 0;
8
}
9
}
Джава
xxxxxxxxxx
1
private int updateEmployee(Employee employee) {
2
try {
3
return jdbcTemplate.update("UPDATE employees SET lastname = ?, firstname = ?
4
WHERE email = ?", employee.getLastname(), employee.getFirstname(),
5
employee.getEmail());
6
} catch (Exception e) {
7
return 0;
8
}
9
}
Теперь, когда мы настроили класс EmployeeService , нам нужно подключить пользовательский интерфейс (кнопка Сохранить) для работы. Откройте файл MasterDetailView.java (с помощью класса MasterDetailView ). В конструкторе этого класса вы можете найти строку кода, которая запускается при нажатии кнопки Сохранить. В примере это что-то вроде этого:
Джава
xxxxxxxxxx
1
save.addClickListener(e -> { Notification.show("Not implemented"); });
Этот код означает, что при нажатии кнопки «Сохранить» запускается код Notification.show («Не реализовано»); Мы собираемся заменить отображение уведомления на фактическое сохранение (или вставку) информации о сотрудниках в базу данных. Как это:
Джава
xxxxxxxxxx
1
save.addClickListener(
2
e -> { Employee employee = binder.getBean();
3
if ( employeeService.saveEmployee(employee) > 0) {
4
employees.setItems(employeeService.findAll());
5
} else {
6
Notification.show("Save error");
7
}
8
}
9
);
Этот блок кода использует связыватель для получения информации о сотрудниках, которая в данный момент отображается в форме с информацией о сотрудниках (не в списке, а в полях справа). Затем он вызывает saveEmployee
метод класса EmployeeService для сохранения сотрудника. Если saveEmployee
метод возвращает значение больше 0, мы обновляем команду сотрудников setItem
командой. В противном случае пользователю будет показано уведомление с ошибкой сохранения текста.
Есть еще один шаг, который вам нужно сделать: найти populateForm
метод в классе MasterDetailView . Убедитесь, что он существует, и, если да, измените следующую строку кода:
Джава
xxxxxxxxxx
1
binder.readBean(value);
в
Джава
xxxxxxxxxx
1
binder.setBean(value);
Это важный шаг, и без связывания, установленного с помощью setBean
метода, мы не сможем получить значения из связывателя с помощью binder.getBean()
команды при нажатии кнопки Сохранить.
Теперь у нас должно быть все ясно, чтобы протестировать приложение. Не забудьте сохранить каждый файл в IDE и запустить приложение. Когда приложение запустится, перейдите на страницу MasterDetail , выберите сотрудника из списка, измените имя и фамилию этого сотрудника и нажмите кнопку Сохранить. Если все прошло хорошо, поля ввода должны быть очищены, а имя этого сотрудника должно быть изменено, а адрес электронной почты остается прежним.
Теперь возьмите другую строку из списка и измените имена и адрес электронной почты (но используйте совершенно новый адрес электронной почты, которого еще нет в списке). Затем нажмите кнопку Сохранить. Новая строка должна появиться в списке. Теперь поиграйте со списком, выберите строку и измените адрес электронной почты на тот, который уже есть в списке, но для другого человека.
Что происходит, когда вы меняете чужой адрес электронной почты в соответствии с этим? Вы должны увидеть, что логика не существует для пользователя. Постарайтесь выяснить, почему это происходит, и как вы можете предотвратить это. Кроме того, выбор сотрудника из списка и затем нажатие кнопки «Очистить» приводит к удалению информации об этом сотруднике из списка. Он не удаляет сотрудника и не сохраняет очищенные данные, но это не совсем тот результат, которого ожидает конечный пользователь.
8. Добавление информации о сотруднике
Возможно, вы уже заметили, что если вы нажмете кнопку «Очистить», а затем попытаетесь ввести совершенно новую информацию о сотруднике, это не сработает. То же самое происходит при перезагрузке страницы, и вы начинаете вставлять информацию о сотруднике без предварительного выбора сотрудника. Почему это?
Итак, Ваадин помогает вам с использованием связующих веществ. Вы связываете поля ввода информации о сотруднике с этим конкретным сотрудником. Вы можете вернуть информацию из всех полей с помощью простой binder.getBean
команды вместо того, чтобы запрашивать значение у каждого поля в отдельности. Приложение связывает сотрудника с полями ввода, когда вы выбираете его из списка. Это делается в конструкторе класса MasterDetailView со следующей строкой:
Джава
xxxxxxxxxx
1
employees.asSingleSelect().addValueChangeListener(
2
event -> populateForm(event.getValue())
3
);
Это означает, что при выделении одной строки из списка мы вызываем метод populateForm. Если вы хотите узнать, что происходит более подробно, снова взгляните на метод populateForm. Оттуда вы можете найти добавленную binder.setBean(value);
строку кода. Это связывает информацию о выбранном сотруднике с полями.
Сначала мы решим проблему, заключающуюся в том, что когда вы обновляете страницу (не выбирая сотрудника из списка), заполняете информацию о сотруднике в пустые поля и нажимаете сохранить, ничего не происходит. Основной причиной этого является то, что поля ввода не связаны связывателем с любым сотрудником. Когда вы нажимаете кнопку Сохранить, и приложение запускает код Employee employee = binder.getBean();
в прослушивателе щелчка кнопки Сохранить. Поскольку связыватель не устанавливается с помощью команды setBean, он возвращает ноль. Затем значение Null передается команде employeeService.saveEmployee в качестве параметра. Этот нуль затем используется в методе saveEmployee сemployee.getEmail()
, но так как null не имеет метода getEmail, он выдает исключение null и выполнение кода останавливается. Вы можете проверить это в консоли вашей IDE или там, где вы выходите из сообщений об ошибках из приложения.
Мы можем справиться с несуществующей привязкой разными способами; Можно было бы проверить, является ли bean-компонент сотрудника, полученный из связывателя, нулевым, и создать новый bean-компонент сотрудника, который мы передаем. Это потребовало бы от нас получить всю информацию из полей ввода. Теперь у нас есть только пара, но если бы были десятки полей ввода, это может быть много работы.
Вместо этого мы с самого начала установим связующее с пустым компонентом сотрудника. Этот пустой компонент затем заменяется новым, если в списке выбран сотрудник. Найдите конструктор класса MasterDetailView (мы уже обсуждали это ранее). Найдите строку кода binder.bindInstanceFields(this);
и добавьте следующую строку кода после этого:
Джава
xxxxxxxxxx
1
binder.setBean(new Employee());
Эта строка кода связывает вновь созданного сотрудника с полями ввода. Если вы хотите инициализировать сотрудника по умолчанию значениями, вы можете использовать конструктор класса Employee, который принимает имя, фамилию, адрес электронной почты и заголовок в качестве параметра. Заголовок вообще не использовался в этом уроке, но в этом случае вам также необходимо это указать. В качестве упражнения вы можете добавить новый конструктор в класс Employee без заголовка. Если вы решите использовать конструктор с начальными значениями в качестве имен и адреса электронной почты после обновления браузера, вы можете увидеть заданные значения уже в полях ввода.
Теперь, если вы запустите приложение, обновите браузер, введите фамилию, имя, адрес электронной почты и нажмите кнопку «Сохранить», будет создан сотрудник с данной информацией. Конечно, если вы введете уже существующий адрес электронной почты, он не будет вставлен, а обновлен. Но вы уже знали это раньше.
Теперь мы рассмотрим вторую проблему с добавлением новых сотрудников. Если вы выбираете сотрудника из списка и нажимаете сохранить или очистить, поля ввода очищаются. Если вы сейчас попытаетесь добавить новую информацию о сотруднике и нажмете кнопку «Сохранить», вы получите то же исключение нулевого указателя. Почему это?
Это потому, что когда вы нажимаете кнопку «Очистить», он вызывает код employees.asSingleSelect().clear()
, который очищает выбор из списка сотрудников. Когда это происходит, вызывается строка кода populateForm(event.getValue())
. Поскольку выбранное значение очищается первой командой, метод populateForm получает параметр null (поскольку ничего не выбрано). В методе populateForm код binder.setBean(value);
устанавливает для связывателя значение null. Это та же самая ситуация, которая была у нас при обновлении страницы и добавлении сотрудника без предварительного нажатия на строку в списке. Установка связывателя на ноль также происходит, когда вы нажимаете кнопку Сохранить. Цепочка команд немного длиннее, но конечный результат такой же, как с кнопкой «Очистить».
Мы можем предотвратить это разными способами. Опять же, это руководство не о лучших практиках, а о том, как быстро начать работу. Имея это в виду, мы собираемся использовать следующее решение; поскольку обе наши проблемы (нажатие кнопки очистки и кнопки сохранения) приводят нас к методу populateForm, мы разместим исправление там. Когда метод будет запущен, мы проверим, имеет ли значение параметра Employee значение null. Если это не нуль, мы продолжаем как прежде. Если оно равно null, мы создадим сотрудника и свяжем его. По сути, это то же самое, что мы делали раньше в конструкторе. Добавьте следующий код в метод populateForm:
Джава
xxxxxxxxxx
1
if ( value == null ) { value = new Employee(); }
Не забудьте добавить его перед строкой кода:
Джава
xxxxxxxxxx
1
binder.setBean(value);
Теперь к связующему всегда должен быть привязан bean-компонент сотрудника, и эти исключения нулевого указателя должны заканчиваться. Это означает, что вы можете вставить новых сотрудников в пустые поля. Эта проблема с обновлением информации о сотруднике, если адрес электронной почты уже используется, все еще существует, но это уже другая история.
9. Удаление сотрудника
CRUD означает создание, чтение, обновление и удаление. У нас уже есть Создать (вставить), Читать (выбрать) и Обновить (обновить), но нам не хватает Удалить. Это требует, чтобы мы поместили новую кнопку в пользовательский интерфейс и создали несколько методов для удаления чего-либо из базы данных.
Сначала мы начнем с необходимых методов. Поскольку у нас есть метод saveEmployee, у нас также может быть метод deleteEmployee. Откройте класс EmployeeService и добавьте следующий метод:
Джава
xxxxxxxxxx
1
public int deleteEmployee(Employee employee) {
2
try {
3
return jdbcTemplate.update("DELETE FROM employees WHERE email = ?",
4
employee.getEmail());
5
} catch (Exception e) {
6
return 0;
7
}
8
}
Это удаляет из базы данных все строки, которые имеют тот же адрес электронной почты, что и данный сотрудник. Это означает, что если у вас есть несколько сотрудников с одним и тем же адресом электронной почты, они будут удалены. Звучит опасно, но, как вы помните, мы согласились использовать электронную почту в качестве ключа, и в базе данных должен быть только один сотрудник с таким же адресом электронной почты.
Теперь мы должны разместить кнопку удаления в пользовательском интерфейсе. Правильное место для кнопки удаления может вызвать обсуждение среди разработчиков, дизайнеров и пользователей. На данный момент, мы просто помещаем кнопку в пользовательский интерфейс и не обсуждаем размещение.
Кнопка удаления должна появиться после полей ввода в правой части экрана; та же область, где находятся кнопки «Очистить» и «Сохранить». Сначала добавьте объявление кнопки в качестве переменной в класс MasterDetailView (так же, как кнопки «Сохранить» и «Очистить»).
Джава
xxxxxxxxxx
1
private Button delete = new Button("Delete");
Откройте класс MasterDetailView и найдите createButtonLayout
метод. Мы будем использовать их, чтобы показать кнопку, которая используется для удаления чего-либо. Добавьте строку кода в createButtonLayout:
Джава
xxxxxxxxxx
1
delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
Логическое место для кода до или после того , как cancel.addThemeVariants
и save.addThemeVariants
команд. Затем добавьте кнопку в форму, добавив кнопку к параметрам buttonLayout.add
команды:
Джава
xxxxxxxxxx
1
buttonLayout.add(delete, cancel, save);
Порядок кнопок в параметрах определяет порядок кнопок в пользовательском интерфейсе. Помещение delete в качестве первого параметра также отображает его как первую кнопку слева. При запуске приложения кнопка «Удалить» должна отображаться рядом с кнопками «Отмена» и «Сохранить».
Нажатие кнопки удаления в настоящее время ничего не делает. Нам нужно добавить слушатель события к нему. Нам также нужно добавить код, который запускается, когда происходит событие click. Сделайте это, найдя конструктор класса MasterDetailView . Добавьте следующий код в конструктор. Логическое место находится рядом с тем местом, где определены слушатели событий кнопок сохранения и отмены. Вы уже внесли изменения в прослушиватель событий кнопки сохранения.
Джава
xxxxxxxxxx
1
delete.addClickListener(
2
e -> { Employee employee = binder.getBean();
3
if ( employeeService.deleteEmployee(employee) > 0) {
4
employees.setItems(employeeService.findAll());
5
} else {
6
Notification.show("Delete error");
7
}
8
});
Когда нажата кнопка «Удалить», мы получаем сотрудника, который в настоящее время привязан к полям ввода, и передаем его в качестве параметра методу deleteEmployee. Мы уже закодировали этот метод и знаем, что он берет адрес электронной почты сотрудника и удаляет всех сотрудников с этим адресом электронной почты из базы данных.
Если вы запускаете приложения, вы можете выбрать сотрудника из списка и удалить его (из базы данных и из списка), нажав кнопку удаления. Если ничего не выбрано или данный адрес электронной почты не найден, вы должны увидеть небольшое уведомление (в правом нижнем углу экрана), в котором говорится об ошибке «Удалить».
Теперь попробуйте подать заявку. Удалить всех сотрудников из списка, добавить пару новых, изменить существующих сотрудников и т. Д. После того, как вы удалили всех сотрудников, не должно быть возможности добавить больше сотрудников с дублирующимся адресом электронной почты.
10. Что делать дальше?
Добавление операций CRUD в приложение Vaadin позволяет вам продолжить дальнейшее развитие кроличьей норы в разработке приложений. Есть еще пара вещей, которые вы можете сделать здесь, например, выяснить, как убедиться, что пользователь не попытается случайно добавить новый адрес электронной почты, который уже используется, или как запретить пользователю изменять адрес электронной почты существующего наемный рабочий. У нас также есть поля заголовка и пароля, которые не используются. Я не собираюсь помогать вам с этими идеями здесь, но, надеюсь, у вас теперь есть представление о том, как продолжить.
Спасибо за чтение этого короткого урока.
Сердечно приветствую вас в мире разработки приложений с использованием Vaadin!
11. Проблемы впереди?
Иногда все просто не работает, как планировалось. Если вы не видите список сотрудников в http: // localhost: 8080 / masterdetail, вам следует перепроверить все.
Если список пуст, но все работает нормально, возможно, что-то не так с подключением к базе данных. Мы создали try-catch для операции с базой данных, чтобы вы могли либо отладить код, либо удалить try-catch. В этом случае наличие вещей в try-catch предотвращает показ возможной ошибки пользователю. Удаление try-catch показывает исключение в окне браузера. Это не оптимальное решение, но если у вас возникли проблемы с отладкой, вы можете воспользоваться этой опцией.
Если у вас есть проблема с запросом SQL, вы, вероятно, увидите что-то вроде:
Джава
xxxxxxxxxx
1
There was an exception while trying to navigate to 'masterdetail' with the exception message 'StatementCallback; bad SQL grammar [SELECT firstname, lastname, email, title FROM employeess]; nested exception is java.sql.SQLSyntaxErrorException: Table 'dummydata.employeess' doesn't exist'
или
Джава
xxxxxxxxxx
1
There was an exception while trying to navigate to 'masterdetail' with the exception message 'StatementCallback; bad SQL grammar [SELECT firstname, last_name, email, title FROM employees]; nested exception is java.sql.SQLSyntaxErrorException: Unknown column 'last_name' in 'field list''
Исправление заключается в проверке текста запроса на соответствие именам таблиц и столбцов, которые вы создали в начале.
Если есть проблема с определениями application.properties, вы можете получить исключение:
Джава
xxxxxxxxxx
1
There was an exception while trying to navigate to 'masterdetail' with the exception message 'Failed to obtain JDBC Connection; nested exception is java.sql.SQLSyntaxErrorException: Unknown database 'dummydata2''
Проблема в неправильном имени базы данных (это dummydata2, должно быть dummydata). Проверьте имя базы данных, которую вы создали (или уже сделали), и посмотрите, совпадают ли они.
Еще один может быть:
Джава
xxxxxxxxxx
1
There was an exception while trying to navigate to 'masterdetail' with the exception message 'Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Access denied for user 'dummydata2'@'localhost' (using password: YES)'
Это может быть вызвано неправильным именем пользователя или паролем.
Это также может быть случай, когда пользователь не имеет правильных привилегий.
Во всех случаях дважды проверьте шаги и ваш код.
Дальнейшее чтение
Учебник MySQL: руководство для начинающих изучать MySQL
Пул соединений Tomcat с использованием DBCP и базы данных MySQL [видео]