Привет! Представляю Вашему вниманию перевод руководства «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:
<properties>
<spring.version>5.1.5.RELEASE</spring.version>
<hibernate.version>5.4.1.Final</hibernate.version>
</properties>
Укажите зависимость для Spring Framework:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
Для создания веб-приложений Spring MVC:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
Для использования Spring Data JPA:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
Мы используем Hibernate в качестве реализации JPA, поэтому добавим следующую зависимость:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
Для того, чтобы приложение работало с MySQL, нам нужна зависимость для драйвера MySQL JDBC:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.14</version>
<scope>runtime</scope>
</dependency>
И теперь зависимости для Java Servlet, JSP и JSTL:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
Создайте два Java пакета в корне проекта:
— net.codejava.config
: для классов конфигурации.
— net.codejava.customer
: для классов приложения.
3. Создание файла настройки JPA
Поскольку мы используем JPA, нам нужно определить свойства для подключения к базе данных в файле persistence.xml
, а не в hibernate.cfg.xml
. Создайте новый каталог с именем META-INF
в исходной папке проекта, чтобы поместить в него файл persistence.xml
:
И впишите в этот файл код представленный ниже:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="SalesDB">
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/sales" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="(password)" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
Как вы можете видеть, мы указываем свойства для соединения с базой данных, такие как 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<Customer, Long> {
}
Это почти весь код, который нам нужен для доступа к данным. Просто, согласитесь? С Spring Data JPA нам не нужно писать DAO (Java Data Acces Object) код. Просто объявите интерфейс, расширяющий интерфейс CrudRepository
, в котором определены такие методы CRUD как: save()
, findAll()
, findById()
, deleteById()
и т.д. Во время выполнения Spring Data JPA автоматически сгенерирует код.
Обратите внимание, что мы должны указать тип класса модели и тип поля первичного ключа при расширении CrudRepository
: CrudRepository<Customer, Long>
.
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<Customer> listAll() {
return (List<Customer>) 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<Customer> 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" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Customer Manager</title>
</head>
<body>
<div align="center">
<h2>Customer Manager</h2>
<form method="get" action="search">
<input type="text" name="keyword" />
<input type="submit" value="Search" />
</form>
<h3><a href="/new">New Customer</a></h3>
<table border="1" cellpadding="5">
<tr>
<th>ID</th>
<th>Name</th>
<th>E-mail</th>
<th>Address</th>
<th>Action</th>
</tr>
<c:forEach items="${listCustomer}" var="customer">
<tr>
<td>${customer.id}</td>
<td>${customer.name}</td>
<td>${customer.email}</td>
<td>${customer.address}</td>
<td>
<a href="/edit?id=${customer.id}">Edit</a>
<a href="/delete?id=${customer.id}">Delete</a>
</td>
</tr>
</c:forEach>
</table>
</div>
</body>
</html>
Теперь вы можете запустить веб-приложение. Добавьте несколько строк в таблицу customer и перейдите по адресу http://localhost:8080/CustomerManager/
, и вы увидите что-то похожее:
10. Добавление нового пользователя
Для того, чтобы реализовать функцию создания нового клиента, нам нужно написать два метода-обработчика. И первый из них будет отображать новую форму для добавления клиента:
@RequestMapping("/new")
public String newCustomerForm(Map<String, Object> 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"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>New Customer</title>
</head>
<body>
<div align="center">
<h2>New Customer</h2>
<form:form action="save" method="post" modelAttribute="customer">
<table border="0" cellpadding="5">
<tr>
<td>Name: </td>
<td><form:input path="name" /></td>
</tr>
<tr>
<td>Email: </td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td>Address: </td>
<td><form:input path="address" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Save"></td>
</tr>
</table>
</form:form>
</div>
</body>
</html>
Теперь на главной странице у вас появится ссылка 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"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Edit Customer</title>
</head>
<body>
<div align="center">
<h2>Edit Customer</h2>
<form:form action="save" method="post" modelAttribute="customer">
<table border="0" cellpadding="5">
<tr>
<td>ID: </td>
<td>${customer.id}
<form:hidden path="id"/>
</td>
</tr>
<tr>
<td>Name: </td>
<td><form:input path="name" /></td>
</tr>
<tr>
<td>Email: </td>
<td><form:input path="email" /></td>
</tr>
<tr>
<td>Address: </td>
<td><form:input path="address" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Save"></td>
</tr>
</table>
</form:form>
</div>
</body>
</html>
Нажмите на гиперссылку 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<Customer, Long> {
@Query(value = "SELECT c FROM Customer c WHERE c.name LIKE '%' || :keyword || '%'"
+ " OR c.email LIKE '%' || :keyword || '%'"
+ " OR c.address LIKE '%' || :keyword || '%'")
public List<Customer> search(@Param("keyword") String keyword);
}
Метод search()
— это просто абстрактный метод, аннотированный с помощью @Query
. Поисковый запрос является запросом JPA.
Затем добавьте метод в класс CustomerService
:
public List<Customer> search(String keyword) {
return repo.search(keyword);
}
Теперь добавьте метод-обработчик в класс CustomerController
:
@RequestMapping("/search")
public ModelAndView search(@RequestParam String keyword) {
List<Customer> 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" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Search Result</title>
</head>
<body>
<div align="center">
<h2>Search Result</h2>
<table border="1" cellpadding="5">
<tr>
<th>ID</th>
<th>Name</th>
<th>E-mail</th>
<th>Address</th>
</tr>
<c:forEach items="${result}" var="customer">
<tr>
<td>${customer.id}</td>
<td>${customer.name}</td>
<td>${customer.email}</td>
<td>${customer.address}</td>
</tr>
</c:forEach>
</table>
</div>
</body>
</html>
Для тестирования функции поиска, введите ключевое слово в поле поиска на домашней странице, и нажмите Enter. Вы увидите страницу результата поиска:
Выводы
В данном руководстве вы узнали как можно разработать веб-приложение Spring MVC, используя Spring Data JPA для доступа к данным. Как вы могли видеть, Spring Data JPA значительно уменьшает и упрощает код, который нам нужно писать.
Для сравнения, вот структура проекта в Eclipse IDE:
Благодарю за прочтение!
Автор: Nikita Volkov