[Из песочницы] Пишем простое веб-приложение используя Spring MVC, Spring Data JPA и Hibernate
Привет, Хабр! Представляю Вашему вниманию перевод руководства «Spring MVC + Spring Data JPA + Hibernate — CRUD Example» автора Nam Ha Minh.
В этом руководстве по Java Spring вы узнаете, как настроить Spring MVC приложение для работы с Spring Data JPA, разработав простое веб-приложение, которое позволяет управлять информацией о клиентах.
По завершению этого руководства, вы сможете создать веб-приложение Java основанное на технологиях Spring MVC и Spring Data JPA, которое выглядит следующим образом:
Программы и технологии используемые в этом руководстве: Java 8, Apache Tomcat 9, MySQL Server 5.7, Eclipse IDE 4.7 (Oxygen), Spring Framework 5.1, Hibernate 5.4, Spring Data JPA 2.1.5 и Servlet 3.1.
Начнём с создания базы данных.
1. Создание базы данных
Мы будем использовать MySQL. В нашем пример мы будем работать с данными в таблице customer, которая находится в схеме с именем sales. Таблица customer имеет 4 поля: id, name, email и address:
Вы можете запустить следующий MySQL скрипт для создания схемы и таблицы:
CREATE DATABASE `sales`;
CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`email` varchar(45) NOT NULL,
`address` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. Создание проекта в Eclipse
Создайте Dynamic Web Project в Eclipse, и конвертируйте его в проект Maven: для этого нажмите ПКМ по проекту, выберете Configure > Convert to Maven Project. В открывшемся диалоговом окне Create new POM введите следующую информацию:
— Group Id: net.codejava
— Artifact Id: CustomerManager
Также убедитесь, что версия JRE для проекта Java 8 или новее.
Затем, откройте pom.xml
(файл Maven), чтобы настроить зависимости для этого проекта. Объявите свойства версий для Spring и Hibernate Frameworks:
5.1.5.RELEASE
5.4.1.Final
Укажите зависимость для Spring Framework:
org.springframework
spring-context
${spring.version}
Для создания веб-приложений Spring MVC:
org.springframework
spring-webmvc
${spring.version}
Для использования Spring Data JPA:
org.springframework
spring-orm
${spring.version}
org.springframework.data
spring-data-jpa
2.1.5.RELEASE
Мы используем Hibernate в качестве реализации JPA, поэтому добавим следующую зависимость:
org.hibernate
hibernate-core
${hibernate.version}
Для того, чтобы приложение работало с MySQL, нам нужна зависимость для драйвера MySQL JDBC:
mysql
mysql-connector-java
8.0.14
runtime
И теперь зависимости для Java Servlet, JSP и JSTL:
javax.servlet
javax.servlet-api
3.1.0
provided
javax.servlet.jsp
javax.servlet.jsp-api
2.3.1
provided
jstl
jstl
1.2
Создайте два Java пакета в корне проекта:
— net.codejava.config
: для классов конфигурации.
— net.codejava.customer
: для классов приложения.
3. Создание файла настройки JPA
Поскольку мы используем JPA, нам нужно определить свойства для подключения к базе данных в файле persistence.xml
, а не в hibernate.cfg.xml
. Создайте новый каталог с именем META-INF
в исходной папке проекта, чтобы поместить в него файл persistence.xml
:
И впишите в этот файл код представленный ниже:
Как вы можете видеть, мы указываем свойства для соединения с базой данных, такие как URL, user, password и класс JDBC драйвера. Так же обратите внимание, что имя SalesDB
будет использоваться нами в коде конфигурации.
4. Создание Model Class
Создайте класс Customer
, который сопоставляется с таблицей customer в базе данных следующим образом:
package net.codejava.customer;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String address;
protected Customer() {
}
protected Customer(String name, String email, String address) {
this.name = name;
this.email = email;
this.address = address;
}
// геттеры и сеттеры не показаны для краткости. Создайте их с помощью средств своей IDE, или вручную.
}
Как вы могли увидеть, мы используем аннотацию @Entity
для сопоставления этого класса с таблицей customer (имя класса совпадает с именем таблицы). Все имена полей класса идентичны именам полей в таблице. Поле id
имеет аннотации @Id
и @GeneratedValue
, чтобы указать, что это поле является первичным ключом и его значение генерируется автоматически.
5. Конфигурация Spring MVC и Spring Data JPA
Далее, напишем Java код для настройки Spring MVC и Spring Data JPA. Мы будем использовать конфигурацию на основе Java, так как она проще, чем XML.
Настройка Spring Dispatcher Servlet
Для использования Spring MVC в нашем приложении, нам надо зарегистрировать Spring Dispatcher Servlet при запуске приложения, написав следующий класс:
package net.codejava.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(WebMvcConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"SpringDispatcher", new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Метод onStartup()
этого класса будет автоматически вызываться сервлетом при загрузке приложения. Spring Dispatcher Servlet обарабатывает все запросы cопоставляя URL »/» и ищет конфигурацию в классе WebMvcConfig
, которй описан ниже.
Настройка Spring MVC
Создайте класс WebMvcConfig
в пакете net.codejava.config
, содержащий следующий код:
package net.codejava.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@ComponentScan("net.codejava ")
public class WebMvcConfig {
@Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
Этот класс помечен аннотацией @Configuration
, сообщающей Spring, что это файл конфигурации. Аннотация @ComponentScan
говорит Spring искать классы конфигурации в пакете net.codejava
.
В этом классе мы создаём bean-компонент, который распознаёт представления (View), с помощью указания префикса и суффикса для этих представлений. Поэтому создайте каталог views
внутри каталога WebContent/WEB-INF
для хранения JSP файлов.
Здесь вы можете добавить и другие конфигурации Spring MVC.
Настройка Spring Data JPA
Для работы с Spring Data JPA нам надо создать два beans-компонента: EntityManagerFactory
и JpaTransactionManager
. Поэтому создадим другой конфигурационный класс JpaConfig
:
package net.codejava.config;
import javax.persistence.EntityManagerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableJpaRepositories(basePackages = {"net.codejava.customer"})
@EnableTransactionManagement
public class JpaConfig {
@Bean
public LocalEntityManagerFactoryBean entityManagerFactory() {
LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName("SalesDB");
return factoryBean;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
Здесь мы используем две важные аннотации:
@EnableJpaRepositories
: сообщает Spring Data JPA, что нужно искать классы репозитория в указанном пакете (net.codejava) для внедрения соответсвующего кода во время выполнения.@EnableTransactionManagement
: сообщает Spring Data JPA, чтобы тот генерировал код для управления транзакциями во время выполнения.
В этом классе первый метод создаёт экземпляр EntityManagerFactory
для управления Persistence Unit нашей SalesDB
(это имя указано выше в persistence.xml
).
Последний метод создаёт экземпляр JpaTransactionManager
для EntityManagerFactory
, созданный методом ранее.
Это минимальная необходимая конфигурация для использования Spring Data JPA.
Создание Repository Interface
Создайте интерфейс CustomerRepository
, который расширяет определенный в Spring Data JPA интерфейс CrudRepository
:
package net.codejava.customer;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface CustomerRepository extends CrudRepository {
}
Это почти весь код, который нам нужен для доступа к данным. Просто, согласитесь? С Spring Data JPA нам не нужно писать DAO (Java Data Acces Object) код. Просто объявите интерфейс, расширяющий интерфейс CrudRepository
, в котором определены такие методы CRUD как: save()
, findAll()
, findById()
, deleteById()
и т.д. Во время выполнения Spring Data JPA автоматически сгенерирует код.
Обратите внимание, что мы должны указать тип класса модели и тип поля первичного ключа при расширении CrudRepository
: CrudRepository
.
7. Создание Service Class
Затем, создайте класс CustomerService
:
package net.codejava.customer;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class CustomerService {
@Autowired CustomerRepository repo;
public void save(Customer customer) {
repo.save(customer);
}
public List listAll() {
return (List) repo.findAll();
}
public Customer get(Long id) {
return repo.findById(id).get();
}
public void delete(Long id) {
repo.deleteById(id);
}
}
Обратите внимание на аннотацию @Transactional
, которой помечен наш класс. Это означает, что все методы этого класса будут перехвачены Spring Data JPA для управления транзакциями. И экземпляр интерфейса CustomerRepository
будет внедрён в этот класс:
@Autowired CustomerRepository repo;
Это похоже на магию, поскольку мы не пишем DAO код, но Spring Data JPA автоматически создаст реализацию во время выполнения.
Как вы можете видеть, все методы в данном классе предназначены для операций CRUD. Он просто делегирует весь вызов объекту CustomerRepository
. Вам может показаться этот класс избыточным, но он необходим, для отделения уровня business/service от уровня repository/DAO.
8. Создание контроллера Spring MVC
Создайте класс CustomerContoroller
для обработки всех запросов от клиентов:
package net.codejava.customer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class CustomerController {
@Autowired
private CustomerService customerService;
// здесь будут методы обработки
}
Это типичный класс Spring MVC Controller, который аннотирован с помощью @Controller
. Вы можете увидеть, что экземпляр CustomerService
внедряется в этот объект с помощью аннотации @Autowired
.
Мы напишем методы обработки в следующих секциях.
9. Добавление списка клиентов
На домашней странице нашего приложения будут отображаться все клиенты, для этого добавьте соответствующий обрабатывающий метод в наш CustomerController
класс:
@RequestMapping("/")
public ModelAndView home() {
List listCustomer = customerService.listAll();
ModelAndView mav = new ModelAndView("index");
mav.addObject("listCustomer", listCustomer);
return mav;
}
Домашняя страница просмотра (index.jsp
) должна выглядеть следующим образом:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
Customer Manager
Customer Manager
New Customer
ID
Name
E-mail
Address
Action
${customer.id}
${customer.name}
${customer.email}
${customer.address}
Edit
Delete
Теперь вы можете запустить веб-приложение. Добавьте несколько строк в таблицу customer и перейдите по адресу http://localhost:8080/CustomerManager/
, и вы увидите что-то похожее:
10. Добавление нового пользователя
Для того, чтобы реализовать функцию создания нового клиента, нам нужно написать два метода-обработчика. И первый из них будет отображать новую форму для добавления клиента:
@RequestMapping("/new")
public String newCustomerForm(Map model) {
Customer customer = new Customer();
model.put("customer", customer);
return "new_customer";
}
Напишем саму JSP форму с именем new_customer.jsp
:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
New Customer
New Customer
Name:
Email:
Address:
Теперь на главной странице у вас появится ссылка New Customer, при нажатии на которую вы увидите новую форму:
Второй метод-обработчик будет обрабатывать кнопку Save в этой форме:
@RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveCustomer(@ModelAttribute("customer") Customer customer) {
customerService.save(customer);
return "redirect:/";
}
Как вы можете видеть, она перенаправляет клиента на домашнюю страницу, после успешного сохранения пользователя.
11. Изменение данных пользователя
Чтобы реализовать функцию редактирования клиента, добавим следующий метод-обработчик в класс CustomerController
:
@RequestMapping("/edit")
public ModelAndView editCustomerForm(@RequestParam long id) {
ModelAndView mav = new ModelAndView("edit_customer");
Customer customer = customerService.get(id);
mav.addObject("customer", customer);
return mav;
}
Напишем форму edit_customer.jsp
, которая вызывается этим методом:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
Edit Customer
Edit Customer
ID:
${customer.id}
Name:
Email:
Address:
Нажмите на гиперссылку Edit рядом с клиентом на домашней странице, вызовется форма редактирования клиента, которая будет выглядеть примерно вот так:
Метод-обработчик по-прежнему обрабатывает кнопку Save.
12. Удаление клиента
Для реализации функции удаления, напишите следующий метод-обработчик в классе CustomerController
:
@RequestMapping("/delete")
public String deleteCustomerForm(@RequestParam long id) {
customerService.delete(id);
return "redirect:/";
}
Нажмите на гиперссылку Delete рядом с клиентом на главной странице. Клиент удалится, а список обновится.
13. Поиск по клиентам
Наконец, давайте реализуем функцию поиска, которая позволяет пользователю искать клиентов, вводя ключевое слово. Функция поиска ищет ключевые слова в трёх полях: имя, email и адрес, для чего нам потребуется написать собственный метод в интерфейсе CustomerRepository
:
package net.codejava.customer;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface CustomerRepository extends CrudRepository {
@Query(value = "SELECT c FROM Customer c WHERE c.name LIKE '%' || :keyword || '%'"
+ " OR c.email LIKE '%' || :keyword || '%'"
+ " OR c.address LIKE '%' || :keyword || '%'")
public List search(@Param("keyword") String keyword);
}
Метод search()
— это просто абстрактный метод, аннотированный с помощью @Query
. Поисковый запрос является запросом JPA.
Затем добавьте метод в класс CustomerService
:
public List search(String keyword) {
return repo.search(keyword);
}
Теперь добавьте метод-обработчик в класс CustomerController
:
@RequestMapping("/search")
public ModelAndView search(@RequestParam String keyword) {
List result = customerService.search(keyword);
ModelAndView mav = new ModelAndView("search");
mav.addObject("result", result);
return mav;
}
И создайте страницу результата поиска search.jsp
:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
Search Result
Search Result
ID
Name
E-mail
Address
${customer.id}
${customer.name}
${customer.email}
${customer.address}
Для тестирования функции поиска, введите ключевое слово в поле поиска на домашней странице, и нажмите Enter. Вы увидите страницу результата поиска:
Выводы
В данном руководстве вы узнали как можно разработать веб-приложение Spring MVC, используя Spring Data JPA для доступа к данным. Как вы могли видеть, Spring Data JPA значительно уменьшает и упрощает код, который нам нужно писать.
Для сравнения, вот структура проекта в Eclipse IDE:
Благодарю за прочтение!